nextcloud/apps/files_external/3rdparty/php-opencloud/lib/OpenCloud/OpenStack.php

1199 lines
35 KiB
PHP

<?php
/**
* PHP OpenCloud library.
*
* @copyright Copyright 2013 Rackspace US, Inc. See COPYING for licensing information.
* @license https://www.apache.org/licenses/LICENSE-2.0 Apache 2.0
* @version 1.6.0
* @author Glen Campbell <glen.campbell@rackspace.com>
* @author Jamie Hannaford <jamie.hannaford@rackspace.com>
*/
namespace OpenCloud;
require_once __DIR__ . '/Globals.php';
use OpenCloud\Common\Base;
use OpenCloud\Common\Lang;
use OpenCloud\Common\Exceptions;
use OpenCloud\Common\ServiceCatalogItem;
/**
* The OpenStack class represents a relationship (or "connection")
* between a user and a service.
*
* This is the primary entry point into an OpenStack system, and the only one
* where the developer is required to know and provide the endpoint URL (in
* all other cases, the endpoint is derived from the Service Catalog provided
* by the authentication system).
*
* Since various providers have different mechanisms for authentication, users
* will often use a subclass of OpenStack. For example, the Rackspace
* class is provided for users of Rackspace's cloud services, and other cloud
* providers are welcome to add their own subclasses as well.
*
* General usage example:
* <code>
* $username = 'My Username';
* $secret = 'My Secret';
* $connection = new OpenCloud\OpenStack($username, $secret);
* // having established the connection, we can set some defaults
* // this sets the default name and region of the Compute service
* $connection->SetDefaults('Compute', 'cloudServersOpenStack', 'ORD');
* // access a Compute service
* $chicago = $connection->Compute();
* // if we want to access a different service, we can:
* $dallas = $connection->Compute('cloudServersOpenStack', 'DFW');
* </code>
*/
class OpenStack extends Base
{
/**
* This holds the HTTP User-Agent: used for all requests to the services. It
* is public so that, if necessary, it can be entirely overridden by the
* developer. However, it's strongly recomended that you use the
* appendUserAgent() method to APPEND your own User Agent identifier to the
* end of this string; the user agent information can be very valuable to
* service providers to track who is using their service.
*
* @var string
*/
public $useragent = RAXSDK_USER_AGENT;
protected $url;
protected $secret = array();
protected $token;
protected $expiration = 0;
protected $tenant;
protected $catalog;
protected $connectTimeout = RAXSDK_CONNECTTIMEOUT;
protected $httpTimeout = RAXSDK_TIMEOUT;
protected $overlimitTimeout = RAXSDK_OVERLIMIT_TIMEOUT;
/**
* This associative array holds default values used to identify each
* service (and to select it from the Service Catalog). Use the
* Compute::SetDefaults() method to change the default values, or
* define the global constants (for example, RAXSDK_COMPUTE_NAME)
* BEFORE loading the OpenCloud library:
*
* <code>
* define('RAXSDK_COMPUTE_NAME', 'cloudServersOpenStack');
* include('openstack.php');
* </code>
*/
protected $defaults = array(
'Compute' => array(
'name' => RAXSDK_COMPUTE_NAME,
'region' => RAXSDK_COMPUTE_REGION,
'urltype' => RAXSDK_COMPUTE_URLTYPE
),
'ObjectStore' => array(
'name' => RAXSDK_OBJSTORE_NAME,
'region' => RAXSDK_OBJSTORE_REGION,
'urltype' => RAXSDK_OBJSTORE_URLTYPE
),
'Database' => array(
'name' => RAXSDK_DATABASE_NAME,
'region' => RAXSDK_DATABASE_REGION,
'urltype' => RAXSDK_DATABASE_URLTYPE
),
'Volume' => array(
'name' => RAXSDK_VOLUME_NAME,
'region' => RAXSDK_VOLUME_REGION,
'urltype' => RAXSDK_VOLUME_URLTYPE
),
'LoadBalancer' => array(
'name' => RAXSDK_LBSERVICE_NAME,
'region' => RAXSDK_LBSERVICE_REGION,
'urltype' => RAXSDK_LBSERVICE_URLTYPE
),
'DNS' => array(
'name' => RAXSDK_DNS_NAME,
'region' => RAXSDK_DNS_REGION,
'urltype' => RAXSDK_DNS_URLTYPE
),
'Orchestration' => array(
'name' => RAXSDK_ORCHESTRATION_NAME,
'region' => RAXSDK_ORCHESTRATION_REGION,
'urltype' => RAXSDK_ORCHESTRATION_URLTYPE
),
'CloudMonitoring' => array(
'name' => RAXSDK_MONITORING_NAME,
'region' => RAXSDK_MONITORING_REGION,
'urltype' => RAXSDK_MONITORING_URLTYPE
),
'Autoscale' => array(
'name' => RAXSDK_AUTOSCALE_NAME,
'region' => RAXSDK_AUTOSCALE_REGION,
'urltype' => RAXSDK_AUTOSCALE_URLTYPE
)
);
private $_user_write_progress_callback_func;
private $_user_read_progress_callback_func;
/**
* Tracks file descriptors used by streaming downloads
*
* This will permit multiple simultaneous streaming downloads; the
* key is the URL of the object, and the value is its file descriptor.
*
* To prevent memory overflows, each array element is deleted when
* the end of the file is reached.
*/
private $fileDescriptors = array();
/**
* array of options to pass to the CURL request object
*/
private $curlOptions = array();
/**
* list of attributes to export/import
*/
private $exportItems = array(
'token',
'expiration',
'tenant',
'catalog'
);
/**
* Creates a new OpenStack object
*
* The OpenStack object needs two bits of information: the URL to
* authenticate against, and a "secret", which is an associative array
* of name/value pairs. Usually, the secret will be a username and a
* password, but other values may be required by different authentication
* systems. For example, OpenStack Keystone requires a username and
* password, but Rackspace uses a username, tenant ID, and API key.
* (See OpenCloud\Rackspace for that.)
*
* @param string $url - the authentication endpoint URL
* @param array $secret - an associative array of auth information:
* * username
* * password
* @param array $options - CURL options to pass to the HttpRequest object
*/
public function __construct($url, array $secret, array $options = array())
{
// check for supported version
// @codeCoverageIgnoreStart
$version = phpversion();
if ($version < '5.3.1') {
throw new Exceptions\UnsupportedVersionError(sprintf(
Lang::translate('PHP version [%s] is not supported'),
$version
));
}
// @codeCoverageIgnoreEnd
// Start processing
$this->getLogger()->info(Lang::translate('Initializing OpenStack client'));
// Set properties
$this->setUrl($url);
$this->setSecret($secret);
$this->setCurlOptions($options);
}
/**
* Set user agent.
*
* @param string $useragent
* @return OpenCloud\OpenStack
*/
public function setUserAgent($useragent)
{
$this->useragent = $useragent;
return $this;
}
/**
* Allows the user to append a user agent string
*
* Programs that are using these bindings are encouraged to add their
* user agent to the one supplied by this SDK. This will permit cloud
* providers to track users so that they can provide better service.
*
* @param string $agent an arbitrary user-agent string; e.g. "My Cloud App"
* @return OpenCloud\OpenStack
*/
public function appendUserAgent($useragent)
{
$this->useragent .= ';' . $useragent;
return $this;
}
/**
* Get user agent.
*
* @return string
*/
public function getUserAgent()
{
return $this->useragent;
}
/**
* Sets the URL which the client will access.
*
* @param string $url
* @return OpenCloud\OpenStack
*/
public function setUrl($url)
{
$this->url = $url;
return $this;
}
/**
* Get the URL.
*
* @return string
*/
public function getUrl()
{
return $this->url;
}
/**
* Set the secret for the client.
*
* @param array $secret
* @return OpenCloud\OpenStack
*/
public function setSecret(array $secret = array())
{
$this->secret = $secret;
return $this;
}
/**
* Get the secret.
*
* @return array
*/
public function getSecret()
{
return $this->secret;
}
/**
* Set the token for this client.
*
* @param string $token
* @return OpenCloud\OpenStack
*/
public function setToken($token)
{
$this->token = $token;
return $this;
}
/**
* Get the token for this client.
*
* @return string
*/
public function getToken()
{
return $this->token;
}
/**
* Set the expiration for this token.
*
* @param int $expiration
* @return OpenCloud\OpenStack
*/
public function setExpiration($expiration)
{
$this->expiration = $expiration;
return $this;
}
/**
* Get the expiration time.
*
* @return int
*/
public function getExpiration()
{
return $this->expiration;
}
/**
* Set the tenant for this client.
*
* @param string $tenant
* @return OpenCloud\OpenStack
*/
public function setTenant($tenant)
{
$this->tenant = $tenant;
return $this;
}
/**
* Get the tenant for this client.
*
* @return string
*/
public function getTenant()
{
return $this->tenant;
}
/**
* Set the service catalog.
*
* @param mixed $catalog
* @return OpenCloud\OpenStack
*/
public function setCatalog($catalog)
{
$this->catalog = $catalog;
return $this;
}
/**
* Get the service catalog.
*
* @return array
*/
public function getCatalog()
{
return $this->catalog;
}
/**
* Set (all) the cURL options.
*
* @param array $options
* @return OpenCloud\OpenStack
*/
public function setCurlOptions(array $options)
{
$this->curlOptions = $options;
return $this;
}
/**
* Get the cURL options.
*
* @return array
*/
public function getCurlOptions()
{
return $this->curlOptions;
}
/**
* Set a specific file descriptor (associated with a URL)
*
* @param string $key
* @param resource $value
* @return OpenCloud\OpenStack
*/
public function setFileDescriptor($key, $value)
{
$this->descriptors[$key] = $value;
return $this;
}
/**
* Get a specific file descriptor (associated with a URL)
*
* @param string $key
* @return resource|false
*/
public function getFileDescriptor($key)
{
return (!isset($this->descriptors[$key])) ? false : $this->descriptors[$key];
}
/**
* Get the items to be exported.
*
* @return array
*/
public function getExportItems()
{
return $this->exportItems;
}
/**
* Sets the connect timeout.
*
* @param int $timeout
* @return OpenCloud\OpenStack
*/
public function setConnectTimeout($timeout)
{
$this->connectTimeout = $timeout;
return $this;
}
/**
* Get the connect timeout.
*
* @return int
*/
public function getConnectTimeout()
{
return $this->connectTimeout;
}
/**
* Set the HTTP timeout.
*
* @param int $timeout
* @return OpenCloud\OpenStack
*/
public function setHttpTimeout($timeout)
{
$this->httpTimeout = $timeout;
return $this;
}
/**
* Get the HTTP timeout.
*
* @return int
*/
public function getHttpTimeout()
{
return $this->httpTimeout;
}
/**
* Set the overlimit timeout.
*
* @param int $timeout
* @return OpenCloud\OpenStack
*/
public function setOverlimitTimeout($timeout)
{
$this->overlimitTimeout = $timeout;
return $this;
}
/**
* Get the overlimit timeout.
*
* @return int
*/
public function getOverlimitTimeout()
{
return $this->overlimitTimeout;
}
/**
* Sets default values (an array) for a service. Each array must contain a
* "name", "region" and "urltype" key.
*
* @param string $service
* @param array $value
* @return OpenCloud\OpenStack
*/
public function setDefault($service, array $value = array())
{
if (isset($value['name']) && isset($value['region']) && isset($value['urltype'])) {
$this->defaults[$service] = $value;
}
return $this;
}
/**
* Get a specific default value for a service. If none exist, return FALSE.
*
* @param string $service
* @return array|false
*/
public function getDefault($service)
{
return (!isset($this->defaults[$service])) ? false : $this->defaults[$service];
}
/**
* Sets the timeouts for the current connection
*
* @api
* @param integer $t_http the HTTP timeout value (the max period that
* the OpenStack object will wait for any HTTP request to complete).
* Value is in seconds.
* @param integer $t_conn the Connect timeout value (the max period
* that the OpenStack object will wait to establish an HTTP
* connection). Value is in seconds.
* @param integer $t_overlimit the overlimit timeout value (the max period
* that the OpenStack object will wait to retry on an overlimit
* condition). Value is in seconds.
* @return void
*/
public function setTimeouts($httpTimeout, $connectTimeout = null, $overlimitTimeout = null)
{
$this->setHttpTimeout($httpTimeout);
if (isset($connectTimeout)) {
$this->setConnectTimeout($connectTimeout);
}
if (isset($overlimitTimeout)) {
$this->setOverlimitTimeout($overlimitTimeout);
}
}
/**
* Returns the URL of this object
*
* @api
* @param string $subresource specified subresource
* @return string
*/
public function url($subresource='tokens')
{
return Lang::noslash($this->url) . '/' . $subresource;
}
/**
* Returns the stored secret
*
* @return array
*/
public function secret()
{
return $this->getSecret();
}
/**
* Re-authenticates session if expired.
*/
public function checkExpiration()
{
if ($this->hasExpired()) {
$this->authenticate();
}
}
/**
* Checks whether token has expired.
*
* @return bool
*/
public function hasExpired()
{
return time() > ($this->getExpiration() - RAXSDK_FUDGE);
}
/**
* Returns the cached token; if it has expired, then it re-authenticates
*
* @api
* @return string
*/
public function token()
{
$this->checkExpiration();
return $this->getToken();
}
/**
* Returns the cached expiration time;
* if it has expired, then it re-authenticates
*
* @api
* @return string
*/
public function expiration()
{
$this->checkExpiration();
return $this->getExpiration();
}
/**
* Returns the tenant ID, re-authenticating if necessary
*
* @api
* @return string
*/
public function tenant()
{
$this->checkExpiration();
return $this->getTenant();
}
/**
* Returns the service catalog object from the auth service
*
* @return \stdClass
*/
public function serviceCatalog()
{
$this->checkExpiration();
return $this->getCatalog();
}
/**
* Returns a Collection of objects with information on services
*
* Note that these are informational (read-only) and are not actually
* 'Service'-class objects.
*/
public function serviceList()
{
return new Common\Collection($this, 'ServiceCatalogItem', $this->serviceCatalog());
}
/**
* Creates and returns the formatted credentials to POST to the auth
* service.
*
* @return string
*/
public function credentials()
{
if (isset($this->secret['username']) && isset($this->secret['password'])) {
$credentials = array(
'auth' => array(
'passwordCredentials' => array(
'username' => $this->secret['username'],
'password' => $this->secret['password']
)
)
);
if (isset($this->secret['tenantName'])) {
$credentials['auth']['tenantName'] = $this->secret['tenantName'];
}
return json_encode($credentials);
} else {
throw new Exceptions\CredentialError(
Lang::translate('Unrecognized credential secret')
);
}
}
/**
* Authenticates using the supplied credentials
*
* @api
* @return void
* @throws AuthenticationError
*/
public function authenticate()
{
// try to auth
$response = $this->request(
$this->url(),
'POST',
array('Content-Type'=>'application/json'),
$this->credentials()
);
$json = $response->httpBody();
// check for errors
if ($response->HttpStatus() >= 400) {
throw new Exceptions\AuthenticationError(sprintf(
Lang::translate('Authentication failure, status [%d], response [%s]'),
$response->httpStatus(),
$json
));
}
// Decode and check
$object = json_decode($json);
$this->checkJsonError();
// Save the token information as well as the ServiceCatalog
$this->setToken($object->access->token->id);
$this->setExpiration(strtotime($object->access->token->expires));
$this->setCatalog($object->access->serviceCatalog);
/**
* In some cases, the tenant name/id is not returned
* as part of the auth token, so we check for it before
* we set it. This occurs with pure Keystone, but not
* with the Rackspace auth.
*/
if (isset($object->access->token->tenant)) {
$this->setTenant($object->access->token->tenant->id);
}
}
/**
* Performs a single HTTP request
*
* The request() method is one of the most frequently-used in the entire
* library. It performs an HTTP request using the specified URL, method,
* and with the supplied headers and body. It handles error and
* exceptions for the request.
*
* @api
* @param string url - the URL of the request
* @param string method - the HTTP method (defaults to GET)
* @param array headers - an associative array of headers
* @param string data - either a string or a resource (file pointer) to
* use as the request body (for PUT or POST)
* @return HttpResponse object
* @throws HttpOverLimitError, HttpUnauthorizedError, HttpForbiddenError
*/
public function request($url, $method = 'GET', $headers = array(), $data = null)
{
$this->getLogger()->info('Resource [{url}] method [{method}] body [{body}]', array(
'url' => $url,
'method' => $method,
'data' => $data
));
// get the request object
$http = $this->getHttpRequestObject($url, $method, $this->getCurlOptions());
// set various options
$this->getLogger()->info('Headers: [{headers}]', array(
'headers' => print_r($headers, true)
));
$http->setheaders($headers);
$http->setHttpTimeout($this->getHttpTimeout());
$http->setConnectTimeout($this->getConnectTimeout());
$http->setOption(CURLOPT_USERAGENT, $this->getUserAgent());
// data can be either a resource or a string
if (is_resource($data)) {
// loading from or writing to a file
// set the appropriate callback functions
switch($method) {
// @codeCoverageIgnoreStart
case 'GET':
// need to save the file descriptor
$this->setFileDescriptor($url, $data);
// set the CURL options
$http->setOption(CURLOPT_FILE, $data);
$http->setOption(CURLOPT_WRITEFUNCTION, array($this, '_write_cb'));
break;
// @codeCoverageIgnoreEnd
case 'PUT':
case 'POST':
// need to save the file descriptor
$this->setFileDescriptor($url, $data);
if (!isset($headers['Content-Length'])) {
throw new Exceptions\HttpError(
Lang::translate('The Content-Length: header must be specified for file uploads')
);
}
$http->setOption(CURLOPT_UPLOAD, TRUE);
$http->setOption(CURLOPT_INFILE, $data);
$http->setOption(CURLOPT_INFILESIZE, $headers['Content-Length']);
$http->setOption(CURLOPT_READFUNCTION, array($this, '_read_cb'));
break;
default:
// do nothing
break;
}
} elseif (is_string($data)) {
$http->setOption(CURLOPT_POSTFIELDS, $data);
} elseif (isset($data)) {
throw new Exceptions\HttpError(
Lang::translate('Unrecognized data type for PUT/POST body, must be string or resource')
);
}
// perform the HTTP request; returns an HttpResult object
$response = $http->execute();
// handle and retry on overlimit errors
if ($response->httpStatus() == 413) {
$object = json_decode($response->httpBody());
$this->checkJsonError();
// @codeCoverageIgnoreStart
if (isset($object->overLimit)) {
/**
* @TODO(glen) - The documentation says "retryAt", but
* the field returned is "retryAfter". If the doc changes,
* then there's no problem, but we'll need to fix this if
* they change the code to match the docs.
*/
$retryAfter = $object->overLimit->retryAfter;
$sleepInterval = strtotime($retryAfter) - time();
if ($sleepInterval && $sleepInterval <= $this->getOverlimitTimeout()) {
sleep($sleepInterval);
$response = $http->Execute();
} else {
throw new Exceptions\HttpOverLimitError(sprintf(
Lang::translate('Over limit; next available request [%s][%s] is not for [%d] seconds at [%s]'),
$method,
$url,
$sleepInterval,
$retryAfter
));
}
}
// @codeCoverageIgnoreEnd
}
// do some common error checking
switch ($response->httpStatus()) {
case 401:
throw new Exceptions\HttpUnauthorizedError(sprintf(
Lang::translate('401 Unauthorized for [%s] [%s]'),
$url,
$response->HttpBody()
));
break;
case 403:
throw new Exceptions\HttpForbiddenError(sprintf(
Lang::translate('403 Forbidden for [%s] [%s]'),
$url,
$response->HttpBody()
));
break;
case 413: // limit
throw new Exceptions\HttpOverLimitError(sprintf(
Lang::translate('413 Over limit for [%s] [%s]'),
$url,
$response->HttpBody()
));
break;
default:
// everything is fine here, we're fine, how are you?
break;
}
// free the handle
$http->close();
// return the HttpResponse object
$this->getLogger()->info('HTTP STATUS [{code}]', array(
'code' => $response->httpStatus()
));
return $response;
}
/**
* Sets default values for name, region, URL type for a service
*
* Once these are set (and they can also be set by defining global
* constants), then you do not need to specify these values when
* creating new service objects.
*
* @api
* @param string $service the name of a supported service; e.g. 'Compute'
* @param string $name the service name; e.g., 'cloudServersOpenStack'
* @param string $region the region name; e.g., 'LON'
* @param string $urltype the type of URL to use; e.g., 'internalURL'
* @return void
* @throws UnrecognizedServiceError
*/
public function setDefaults(
$service,
$name = null,
$region = null,
$urltype = null
) {
if (!isset($this->defaults[$service])) {
throw new Exceptions\UnrecognizedServiceError(sprintf(
Lang::translate('Service [%s] is not recognized'), $service
));
}
if (isset($name)) {
$this->defaults[$service]['name'] = $name;
}
if (isset($region)) {
$this->defaults[$service]['region'] = $region;
}
if (isset($urltype)) {
$this->defaults[$service]['urltype'] = $urltype;
}
}
/**
* Allows the user to define a function for tracking uploads
*
* This can be used to implement a progress bar or similar function. The
* callback function is called with a single parameter, the length of the
* data that is being uploaded on this call.
*
* @param callable $callback the name of a global callback function, or an
* array($object, $functionname)
* @return void
*/
public function setUploadProgressCallback($callback)
{
$this->_user_write_progress_callback_func = $callback;
}
/**
* Allows the user to define a function for tracking downloads
*
* This can be used to implement a progress bar or similar function. The
* callback function is called with a single parameter, the length of the
* data that is being downloaded on this call.
*
* @param callable $callback the name of a global callback function, or an
* array($object, $functionname)
* @return void
*/
public function setDownloadProgressCallback($callback)
{
$this->_user_read_progress_callback_func = $callback;
}
/**
* Callback function to handle reads for file uploads
*
* Internal function for handling file uploads. Note that, although this
* function's visibility is public, this is only because it must be called
* from the HttpRequest interface. This should NOT be called by users
* directly.
*
* @param resource $ch a CURL handle
* @param resource $fd a file descriptor
* @param integer $length the amount of data to read
* @return string the data read
* @codeCoverageIgnore
*/
public function _read_cb($ch, $fd, $length)
{
$data = fread($fd, $length);
$len = strlen($data);
if (isset($this->_user_write_progress_callback_func)) {
call_user_func($this->_user_write_progress_callback_func, $len);
}
return $data;
}
/**
* Callback function to handle writes for file downloads
*
* Internal function for handling file downloads. Note that, although this
* function's visibility is public, this is only because it must be called
* via the HttpRequest interface. This should NOT be called by users
* directly.
*
* @param resource $ch a CURL handle
* @param string $data the data to be written to a file
* @return integer the number of bytes written
* @codeCoverageIgnore
*/
public function _write_cb($ch, $data)
{
$url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
if (false === ($fp = $this->getFileDescriptor($url))) {
throw new Exceptions\HttpUrlError(sprintf(
Lang::translate('Cannot find file descriptor for URL [%s]'), $url)
);
}
$dlen = strlen($data);
fwrite($fp, $data, $dlen);
// call used callback function
if (isset($this->_user_read_progress_callback_func)) {
call_user_func($this->_user_read_progress_callback_func, $dlen);
}
// MUST return the length to CURL
return $dlen;
}
/**
* exports saved token, expiration, tenant, and service catalog as an array
*
* This could be stored in a cache (APC or disk file) and reloaded using
* ImportCredentials()
*
* @return array
*/
public function exportCredentials()
{
$this->authenticate();
$array = array();
foreach ($this->getExportItems() as $key) {
$array[$key] = $this->$key;
}
return $array;
}
/**
* imports credentials from an array
*
* Takes the same values as ExportCredentials() and reuses them.
*
* @return void
*/
public function importCredentials(array $values)
{
foreach ($this->getExportItems() as $item) {
$this->$item = $values[$item];
}
}
/********** FACTORY METHODS **********
*
* These methods are provided to permit easy creation of services
* (for example, Nova or Swift) from a connection object. As new
* services are supported, factory methods should be provided here.
*/
/**
* Creates a new ObjectStore object (Swift/Cloud Files)
*
* @api
* @param string $name the name of the Object Storage service to attach to
* @param string $region the name of the region to use
* @param string $urltype the URL type (normally "publicURL")
* @return ObjectStore
*/
public function objectStore($name = null, $region = null, $urltype = null)
{
return $this->service('ObjectStore', $name, $region, $urltype);
}
/**
* Creates a new Compute object (Nova/Cloud Servers)
*
* @api
* @param string $name the name of the Compute service to attach to
* @param string $region the name of the region to use
* @param string $urltype the URL type (normally "publicURL")
* @return Compute
*/
public function compute($name = null, $region = null, $urltype = null)
{
return $this->service('Compute', $name, $region, $urltype);
}
/**
* Creates a new Orchestration (heat) service object
*
* @api
* @param string $name the name of the Compute service to attach to
* @param string $region the name of the region to use
* @param string $urltype the URL type (normally "publicURL")
* @return Orchestration\Service
* @codeCoverageIgnore
*/
public function orchestration($name = null, $region = null, $urltype = null)
{
return $this->service('Orchestration', $name, $region, $urltype);
}
/**
* Creates a new VolumeService (cinder) service object
*
* This is a factory method that is Rackspace-only (NOT part of OpenStack).
*
* @param string $name the name of the service (e.g., 'cloudBlockStorage')
* @param string $region the region (e.g., 'DFW')
* @param string $urltype the type of URL (e.g., 'publicURL');
*/
public function volumeService($name = null, $region = null, $urltype = null)
{
return $this->service('Volume', $name, $region, $urltype);
}
/**
* Generic Service factory method
*
* Contains code reused by the other service factory methods.
*
* @param string $class the name of the Service class to produce
* @param string $name the name of the Compute service to attach to
* @param string $region the name of the region to use
* @param string $urltype the URL type (normally "publicURL")
* @return Service (or subclass such as Compute, ObjectStore)
* @throws ServiceValueError
*/
public function service($class, $name = null, $region = null, $urltype = null)
{
// debug message
$this->getLogger()->info('Factory for class [{class}] [{name}/{region}/{urlType}]', array(
'class' => $class,
'name' => $name,
'region' => $region,
'urlType' => $urltype
));
// Strips off base namespace
$class = preg_replace('#\\\?OpenCloud\\\#', '', $class);
// check for defaults
$default = $this->getDefault($class);
// report errors
if (!$name = $name ?: $default['name']) {
throw new Exceptions\ServiceValueError(sprintf(
Lang::translate('No value for %s name'),
$class
));
}
if (!$region = $region ?: $default['region']) {
throw new Exceptions\ServiceValueError(sprintf(
Lang::translate('No value for %s region'),
$class
));
}
if (!$urltype = $urltype ?: $default['urltype']) {
throw new Exceptions\ServiceValueError(sprintf(
Lang::translate('No value for %s URL type'),
$class
));
}
// return the object
$fullclass = 'OpenCloud\\' . $class . '\\Service';
return new $fullclass($this, $name, $region, $urltype);
}
/**
* returns a service catalog item
*
* This is a helper function used to list service catalog items easily
*/
public function serviceCatalogItem($info = array())
{
return new ServiceCatalogItem($info);
}
}