2016-11-28 21:52:15 -08:00

583 lines
16 KiB
PHP

<?php
/**
* Copyright 2012-2014 Rackspace US, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace OpenCloud;
use Guzzle\Http\Url;
use OpenCloud\Common\Exceptions;
use OpenCloud\Common\Http\Client;
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;
use OpenCloud\Identity\Resource\Token;
use OpenCloud\Identity\Resource\User;
use OpenCloud\Identity\Service as IdentityService;
use Psr\Log\LoggerInterface;
define('RACKSPACE_US', 'https://identity.api.rackspacecloud.com/v2.0/');
define('RACKSPACE_UK', 'https://lon.identity.api.rackspacecloud.com/v2.0/');
/**
* The main client of the library. This object is the central point of negotiation between your application and the
* API because it handles all of the HTTP transactions required to perform operations. It also manages the services
* for your application through convenient factory methods.
*/
class OpenStack extends Client
{
/**
* @var array Credentials passed in by the user
*/
private $secret = array();
/**
* @var string The token produced by the API
*/
private $token;
/**
* @var string The unique identifier for who's accessing the API
*/
private $tenant;
/**
* @var \OpenCloud\Common\Service\Catalog The catalog of services which are provided by the API
*/
private $catalog;
/**
* @var LoggerInterface The object responsible for logging output
*/
private $logger;
/**
* @var string The endpoint URL used for authentication
*/
private $authUrl;
/**
* @var \OpenCloud\Identity\Resource\User
*/
private $user;
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);
parent::__construct($url, $options);
$this->addSubscriber(RequestSubscriber::getInstance());
$this->setDefaultOption('headers/Accept', 'application/json');
}
/**
* Set the credentials for the client
*
* @param array $secret
* @return $this
*/
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. If a string is passed in, the SDK assumes you want to set the ID of the full Token object
* and sets this property accordingly. For any other data type, it assumes you want to populate the Token object.
* This ambiguity arises due to backwards compatibility.
*
* @param string $token
* @return $this
*/
public function setToken($token)
{
$identity = IdentityService::factory($this);
if (is_string($token)) {
if (!$this->token) {
$this->setTokenObject($identity->resource('Token'));
}
$this->token->setId($token);
} else {
$this->setTokenObject($identity->resource('Token', $token));
}
return $this;
}
/**
* Get the token ID for this client.
*
* @return string
*/
public function getToken()
{
return ($this->getTokenObject()) ? $this->getTokenObject()->getId() : null;
}
/**
* Set the full token object
*/
public function setTokenObject(Token $token)
{
$this->token = $token;
}
/**
* Get the full token object.
*/
public function getTokenObject()
{
return $this->token;
}
/**
* @deprecated
*/
public function setExpiration($expiration)
{
$this->getLogger()->warning(Logger::deprecated(__METHOD__, '::getTokenObject()->setExpires()'));
if ($this->getTokenObject()) {
$this->getTokenObject()->setExpires($expiration);
}
return $this;
}
/**
* @deprecated
*/
public function getExpiration()
{
$this->getLogger()->warning(Logger::deprecated(__METHOD__, '::getTokenObject()->getExpires()'));
if ($this->getTokenObject()) {
return $this->getTokenObject()->getExpires();
}
}
/**
* 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
*/
public function setTenant($tenant)
{
$identity = IdentityService::factory($this);
if (is_numeric($tenant) || is_string($tenant)) {
if (!$this->tenant) {
$this->setTenantObject($identity->resource('Tenant'));
}
$this->tenant->setId($tenant);
} else {
$this->setTenantObject($identity->resource('Tenant', $tenant));
}
return $this;
}
/**
* Returns the tenant ID only (backwards compatibility).
*
* @return string
*/
public function getTenant()
{
return ($this->getTenantObject()) ? $this->getTenantObject()->getId() : null;
}
/**
* Set the full Tenant object for this client.
*
* @param OpenCloud\Identity\Resource\Tenant $tenant
*/
public function setTenantObject(Tenant $tenant)
{
$this->tenant = $tenant;
}
/**
* Get the full Tenant object for this client.
*
* @return OpenCloud\Identity\Resource\Tenant
*/
public function getTenantObject()
{
return $this->tenant;
}
/**
* Set the service catalog.
*
* @param mixed $catalog
* @return $this
*/
public function setCatalog($catalog)
{
$this->catalog = Catalog::factory($catalog);
return $this;
}
/**
* Get the service catalog.
*
* @return array
*/
public function getCatalog()
{
return $this->catalog;
}
/**
* @param LoggerInterface $logger
*
* @return $this
*/
public function setLogger(LoggerInterface $logger)
{
$this->logger = $logger;
return $this;
}
/**
* @return LoggerInterface
*/
public function getLogger()
{
if (null === $this->logger) {
$this->setLogger(new Common\Log\Logger);
}
return $this->logger;
}
/**
* @return bool
*/
public function hasLogger()
{
return (null !== $this->logger);
}
/**
* @deprecated
*/
public function hasExpired()
{
$this->getLogger()->warning(Logger::deprecated(__METHOD__, 'getTokenObject()->hasExpired()'));
return $this->getTokenObject() && $this->getTokenObject()->hasExpired();
}
/**
* Formats the credentials array (as a string) for authentication
*
* @return string
* @throws Common\Exceptions\CredentialError
*/
public function getCredentials()
{
if (!empty($this->secret['username']) && !empty($this->secret['password'])) {
$credentials = array('auth' => array(
'passwordCredentials' => array(
'username' => $this->secret['username'],
'password' => $this->secret['password']
)
));
if (!empty($this->secret['tenantName'])) {
$credentials['auth']['tenantName'] = $this->secret['tenantName'];
} elseif (!empty($this->secret['tenantId'])) {
$credentials['auth']['tenantId'] = $this->secret['tenantId'];
}
return json_encode($credentials);
} else {
throw new Exceptions\CredentialError(
Lang::translate('Unrecognized credential secret')
);
}
}
/**
* @param $url
* @return $this
*/
public function setAuthUrl($url)
{
$this->authUrl = Url::factory($url);
return $this;
}
/**
* @return Url
*/
public function getAuthUrl()
{
return $this->authUrl;
}
/**
* Sets the current user based on the generated token.
*
* @param $data Object of user data
*/
public function setUser(User $user)
{
$this->user = $user;
}
/**
* @return \OpenCloud\Identity\Resource\User
*/
public function getUser()
{
return $this->user;
}
/**
* Authenticate the tenant using the supplied credentials
*
* @return void
* @throws AuthenticationError
*/
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());
$body = Formatter::decode($response);
$this->setCatalog($body->access->serviceCatalog);
$this->setTokenObject($identity->resource('Token', $body->access->token));
$this->setUser($identity->resource('User', $body->access->user));
if (isset($body->access->token->tenant)) {
$this->setTenantObject($identity->resource('Tenant', $body->access->token->tenant));
}
// Set X-Auth-Token HTTP request header
$this->updateTokenHeader($this->getToken());
}
/**
* @deprecated
*/
public function getUrl()
{
return $this->getBaseUrl();
}
/**
* Convenience method for exporting current credentials. Useful for local caching.
* @return array
*/
public function exportCredentials()
{
if ($this->hasExpired()) {
$this->authenticate();
}
return array(
'token' => $this->getToken(),
'expiration' => $this->getExpiration(),
'tenant' => $this->getTenant(),
'catalog' => $this->getCatalog()
);
}
/**
* Convenience method for importing credentials. Useful for local caching because it reduces HTTP traffic.
*
* @param array $values
*/
public function importCredentials(array $values)
{
if (!empty($values['token'])) {
$this->setToken($values['token']);
$this->updateTokenHeader($this->getToken());
}
if (!empty($values['expiration'])) {
$this->setExpiration($values['expiration']);
}
if (!empty($values['tenant'])) {
$this->setTenant($values['tenant']);
}
if (!empty($values['catalog'])) {
$this->setCatalog($values['catalog']);
}
}
/**
* Sets the X-Auth-Token header. If no value is explicitly passed in, the current token is used.
*
* @param string $token Value of header.
* @return void
*/
private function updateTokenHeader($token)
{
$this->setDefaultOption('headers/X-Auth-Token', (string) $token);
}
/**
* Creates a new ObjectStore object (Swift/Cloud Files)
*
* @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\ObjectStore\Service
*/
public function objectStoreService($name = null, $region = null, $urltype = null)
{
return ServiceBuilder::factory($this, 'OpenCloud\ObjectStore\Service', array(
'name' => $name,
'region' => $region,
'urlType' => $urltype
));
}
/**
* Creates a new Compute object (Nova/Cloud Servers)
*
* @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\Compute\Service
*/
public function computeService($name = null, $region = null, $urltype = null)
{
return ServiceBuilder::factory($this, 'OpenCloud\Compute\Service', array(
'name' => $name,
'region' => $region,
'urlType' => $urltype
));
}
/**
* Creates a new Orchestration (Heat) 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\Orchestration\Service
* @codeCoverageIgnore
*/
public function orchestrationService($name = null, $region = null, $urltype = null)
{
return ServiceBuilder::factory($this, 'OpenCloud\Orchestration\Service', array(
'name' => $name,
'region' => $region,
'urlType' => $urltype
));
}
/**
* Creates a new Volume (Cinder) 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\Volume\Service
*/
public function volumeService($name = null, $region = null, $urltype = null)
{
return ServiceBuilder::factory($this, 'OpenCloud\Volume\Service', array(
'name' => $name,
'region' => $region,
'urlType' => $urltype
));
}
/**
* Creates a new Rackspace "Cloud Identity" service.
*
* @return \OpenCloud\Identity\Service
*/
public function identityService()
{
$service = IdentityService::factory($this);
$this->authenticate();
return $service;
}
/**
* Creates a new Glance service
*
* @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 Common\Service\ServiceInterface
*/
public function imageService($name = null, $region = null, $urltype = null)
{
return ServiceBuilder::factory($this, 'OpenCloud\Image\Service', array(
'name' => $name,
'region' => $region,
'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
));
}
}