Overview
  • Namespace
  • Class

Namespaces

  • OpenCloud
    • Autoscale
      • Resource
    • CloudMonitoring
      • Collection
      • Exception
      • Resource
    • Common
      • Collection
      • Constants
      • Exceptions
      • Http
        • Message
      • Log
      • Resource
      • Service
    • Compute
      • Constants
      • Exception
      • Resource
    • Database
      • Resource
    • DNS
      • Collection
      • Resource
    • Identity
      • Constants
      • Resource
    • Image
      • Enum
      • Resource
        • JsonPatch
        • Schema
    • LoadBalancer
      • Collection
      • Enum
      • Resource
    • Networking
      • Resource
    • ObjectStore
      • Constants
      • Exception
      • Resource
      • Upload
    • Orchestration
      • Resource
    • Queues
      • Collection
      • Exception
      • Resource
    • Volume
      • Resource

Classes

  • OpenCloud\Volume\Resource\Snapshot
  • OpenCloud\Volume\Resource\Volume
  • OpenCloud\Volume\Resource\VolumeType
  1 <?php
  2 /**
  3  * Copyright 2012-2014 Rackspace US, Inc.
  4  *
  5  * Licensed under the Apache License, Version 2.0 (the "License");
  6  * you may not use this file except in compliance with the License.
  7  * You may obtain a copy of the License at
  8  *
  9  * http://www.apache.org/licenses/LICENSE-2.0
 10  *
 11  * Unless required by applicable law or agreed to in writing, software
 12  * distributed under the License is distributed on an "AS IS" BASIS,
 13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  * See the License for the specific language governing permissions and
 15  * limitations under the License.
 16  */
 17 
 18 namespace OpenCloud\Common\Resource;
 19 
 20 use Guzzle\Http\Url;
 21 use OpenCloud\Common\Constants\State;
 22 use OpenCloud\Common\Exceptions\CreateError;
 23 use OpenCloud\Common\Exceptions\DeleteError;
 24 use OpenCloud\Common\Exceptions\IdRequiredError;
 25 use OpenCloud\Common\Exceptions\NameError;
 26 use OpenCloud\Common\Exceptions\UnsupportedExtensionError;
 27 use OpenCloud\Common\Exceptions\UpdateError;
 28 
 29 abstract class PersistentResource extends BaseResource
 30 {
 31     /**
 32      * Create a new resource
 33      *
 34      * @param array $params
 35      * @return \Guzzle\Http\Message\Response
 36      */
 37     public function create($params = array())
 38     {
 39         // set parameters
 40         if (!empty($params)) {
 41             $this->populate($params, false);
 42         }
 43 
 44         // construct the JSON
 45         $json = json_encode($this->createJson());
 46         $this->checkJsonError();
 47 
 48         $createUrl = $this->createUrl();
 49 
 50         $response = $this->getClient()->post($createUrl, self::getJsonHeader(), $json)->send();
 51 
 52         // We have to try to parse the response body first because it should have precedence over a Location refresh.
 53         // I'd like to reverse the order, but Nova instances return ephemeral properties on creation which are not
 54         // available when you follow the Location link...
 55         if (null !== ($decoded = $this->parseResponse($response))) {
 56             $this->populate($decoded);
 57         } elseif ($location = $response->getHeader('Location')) {
 58             $this->refreshFromLocationUrl($location);
 59         }
 60 
 61         return $response;
 62     }
 63 
 64     /**
 65      * Update a resource
 66      *
 67      * @param array $params
 68      * @return \Guzzle\Http\Message\Response
 69      */
 70     public function update($params = array())
 71     {
 72         // set parameters
 73         if (!empty($params)) {
 74             $this->populate($params);
 75         }
 76 
 77         // construct the JSON
 78         $json = json_encode($this->updateJson($params));
 79         $this->checkJsonError();
 80 
 81         // send the request
 82         return $this->getClient()->put($this->getUrl(), self::getJsonHeader(), $json)->send();
 83     }
 84 
 85     /**
 86      * Delete this resource
 87      *
 88      * @return \Guzzle\Http\Message\Response
 89      */
 90     public function delete()
 91     {
 92         return $this->getClient()->delete($this->getUrl())->send();
 93     }
 94 
 95     /**
 96      * Refresh the state of a resource
 97      *
 98      * @param null $id
 99      * @param null $url
100      * @return \Guzzle\Http\Message\Response
101      * @throws IdRequiredError
102      */
103     public function refresh($id = null, $url = null)
104     {
105         $primaryKey = $this->primaryKeyField();
106         $primaryKeyVal = $this->getProperty($primaryKey);
107 
108         if (!$url) {
109             if (!$id = $id ?: $primaryKeyVal) {
110                 $message = sprintf("This resource cannot be refreshed because it has no %s", $primaryKey);
111                 throw new IdRequiredError($message);
112             }
113 
114             if ($primaryKeyVal != $id) {
115                 $this->setProperty($primaryKey, $id);
116             }
117 
118             $url = $this->getUrl();
119         }
120 
121         // reset status, if available
122         if ($this->getProperty('status')) {
123             $this->setProperty('status', null);
124         }
125 
126         $response = $this->getClient()->get($url)->send();
127 
128         if (null !== ($decoded = $this->parseResponse($response))) {
129             $this->populate($decoded);
130         }
131 
132         return $response;
133     }
134 
135 
136     /**
137      * Causes resource to refresh based on parent's URL
138      */
139     protected function refreshFromParent()
140     {
141         $url = clone $this->getParent()->getUrl();
142         $url->addPath($this->resourceName());
143 
144         $response = $this->getClient()->get($url)->send();
145 
146         if (null !== ($decoded = $this->parseResponse($response))) {
147             $this->populate($decoded);
148         }
149     }
150 
151     /**
152      * Given a `location` URL, refresh this resource
153      *
154      * @param $url
155      */
156     public function refreshFromLocationUrl($url)
157     {
158         $fullUrl = Url::factory($url);
159 
160         $response = $this->getClient()->get($fullUrl)->send();
161 
162         if (null !== ($decoded = $this->parseResponse($response))) {
163             $this->populate($decoded);
164         }
165     }
166 
167     /**
168      * A method to repeatedly poll the API resource, waiting for an eventual state change
169      *
170      * @param null $state    The expected state of the resource
171      * @param null $timeout  The maximum timeout to wait
172      * @param null $callback The callback to use to check the state
173      * @param null $interval How long between each refresh request
174      */
175     public function waitFor($state = null, $timeout = null, $callback = null, $interval = null)
176     {
177         $state    = $state ?: State::ACTIVE;
178         $timeout  = $timeout ?: State::DEFAULT_TIMEOUT;
179         $interval = $interval ?: State::DEFAULT_INTERVAL;
180 
181         // save stats
182         $startTime = time();
183 
184         $states = array('ERROR', $state);
185 
186         while (true) {
187             $this->refresh($this->getProperty($this->primaryKeyField()));
188 
189             if ($callback) {
190                 call_user_func($callback, $this);
191             }
192 
193             if (in_array($this->status(), $states) || (time() - $startTime) > $timeout) {
194                 return;
195             }
196 
197             sleep($interval);
198         }
199     }
200 
201     /**
202      * Provides JSON for create request body
203      *
204      * @return object
205      * @throws \RuntimeException
206      */
207     protected function createJson()
208     {
209         if (!isset($this->createKeys)) {
210             throw new \RuntimeException(sprintf(
211                 'This resource object [%s] must have a visible createKeys array',
212                 get_class($this)
213             ));
214         }
215 
216         $element = (object) array();
217 
218         foreach ($this->createKeys as $key) {
219             if (null !== ($property = $this->getProperty($key))) {
220                 $element->{$this->getAlias($key)} = $this->recursivelyAliasPropertyValue($property);
221             }
222         }
223 
224         if (isset($this->metadata) && count($this->metadata)) {
225             $element->metadata = (object) $this->metadata->toArray();
226         }
227 
228         return (object) array($this->jsonName() => (object) $element);
229     }
230 
231     /**
232      * Returns the alias configured for the given key. If no alias exists
233      * it returns the original key.
234      *
235      * @param  string $key
236      * @return string
237      */
238     protected function getAlias($key)
239     {
240         if (false !== ($alias = array_search($key, $this->aliases))) {
241             return $alias;
242         }
243 
244         return $key;
245     }
246 
247     /**
248      * Returns the given property value's alias, if configured; Else, the
249      * unchanged property value is returned. If the given property value
250      * is an array or an instance of \stdClass, it is aliases recursively.
251      *
252      * @param  mixed $propertyValue Array or \stdClass instance to alias
253      * @return mixed Property value, aliased recursively
254      */
255     protected function recursivelyAliasPropertyValue($propertyValue)
256     {
257         if (is_array($propertyValue)) {
258             foreach ($propertyValue as $key => $subValue) {
259                 $aliasedSubValue = $this->recursivelyAliasPropertyValue($subValue);
260                 if (is_numeric($key)) {
261                     $propertyValue[$key] = $aliasedSubValue;
262                 } else {
263                     unset($propertyValue[$key]);
264                     $propertyValue[$this->getAlias($key)] = $aliasedSubValue;
265                 }
266             }
267         } elseif (is_object($propertyValue) && ($propertyValue instanceof \stdClass)) {
268             foreach ($propertyValue as $key => $subValue) {
269                 unset($propertyValue->$key);
270                 $propertyValue->{$this->getAlias($key)} = $this->recursivelyAliasPropertyValue($subValue);
271             }
272         }
273 
274         return $propertyValue;
275     }
276 
277     /**
278      * Provides JSON for update request body
279      */
280     protected function updateJson($params = array())
281     {
282         if (!isset($this->updateKeys)) {
283             throw new \RuntimeException(sprintf(
284                 'This resource object [%s] must have a visible updateKeys array',
285                 get_class($this)
286             ));
287         }
288 
289         $element = (object) array();
290 
291         foreach ($this->updateKeys as $key) {
292             if (null !== ($property = $this->getProperty($key))) {
293                 $element->{$this->getAlias($key)} = $this->recursivelyAliasPropertyValue($property);
294             }
295         }
296 
297         return (object) array($this->jsonName() => (object) $element);
298     }
299 
300     /**
301      * @throws CreateError
302      */
303     protected function noCreate()
304     {
305         throw new CreateError('This resource does not support the create operation');
306     }
307 
308     /**
309      * @throws DeleteError
310      */
311     protected function noDelete()
312     {
313         throw new DeleteError('This resource does not support the delete operation');
314     }
315 
316     /**
317      * @throws UpdateError
318      */
319     protected function noUpdate()
320     {
321         throw new UpdateError('his resource does not support the update operation');
322     }
323 
324     /**
325      * Check whether an extension is valid
326      *
327      * @param mixed $alias The extension name
328      * @return bool
329      * @throws UnsupportedExtensionError
330      */
331     public function checkExtension($alias)
332     {
333         if (!in_array($alias, $this->getService()->namespaces())) {
334             throw new UnsupportedExtensionError(sprintf("%s extension is not installed", $alias));
335         }
336 
337         return true;
338     }
339 
340     /********  DEPRECATED METHODS ********/
341 
342     /**
343      * @deprecated
344      * @return string
345      * @throws NameError
346      */
347     public function name()
348     {
349         if (null !== ($name = $this->getProperty('name'))) {
350             return $name;
351         } else {
352             throw new NameError('Name attribute does not exist for this resource');
353         }
354     }
355 
356     /**
357      * @deprecated
358      * @return mixed
359      */
360     public function id()
361     {
362         return $this->id;
363     }
364 
365     /**
366      * @deprecated
367      * @return string
368      */
369     public function status()
370     {
371         return (isset($this->status)) ? $this->status : 'N/A';
372     }
373 
374     /**
375      * @deprecated
376      * @return mixed
377      */
378     public function region()
379     {
380         return $this->getService()->region();
381     }
382 
383     /**
384      * @deprecated
385      * @return \Guzzle\Http\Url
386      */
387     public function createUrl()
388     {
389         return $this->getParent()->getUrl($this->resourceName());
390     }
391 }
392 
API documentation generated by ApiGen