Skip to content

Commit

Permalink
Fixed several bugs, including #78, #79 and #80.
Browse files Browse the repository at this point in the history
  • Loading branch information
EdLeafe committed May 31, 2013
1 parent c9ea342 commit 3705cda
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 28 deletions.
3 changes: 3 additions & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Release Notes for pyrax

###2013.05.30 - Version 1.4.2
- Fixed several bugs related to the identity and config file changes.

###2013.05.30 - Version 1.4.1
- Added support for new Cloud Database user APIs.
- Fixed a bug in which an exception class was not defined (#77)
Expand Down
19 changes: 14 additions & 5 deletions docs/pyrax_doc.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,18 +98,27 @@ You don't have to authenticate to each service separately; pyrax handles that fo
## Pyrax Configuration
You can control how pyrax behaves through the configuration file. It should be named `~/.pyrax.cfg`. Like the credential file, `~/.pyrax.cfg` is a standard configuration file. Alternatively, you may set these values using environment variables in the OS. Note that the configuration file values take precendence over any environment variables. Environment variables also do not support multiple configurations.

**NOTE**: At the very least, you *must* set the `identity_type` setting so that can use the correct identity class. Prior versions only worked with Rackspace identity, but that is no longer the case. If you don't want to use a configuration file or an environment variable, you can do this in code:

pyrax.set_setting("identity_type", "keystone")

or

pyrax.set_setting("identity_type", "rackspace")


### Configuration Environments
Pyrax supports multiple configurations, which are referred to as ***envrironments***. An envrironment is a separate OpenStack deployment with which you want to interact. A common situation is when you have a private cloud for some of your work, but also have a public cloud account for the rest. Each of these clouds require different authentication endpoints, and may require different settings for other things such as region, identity type, etc.
Pyrax supports multiple configurations, which are referred to as ***environments***. An environment is a separate OpenStack deployment with which you want to interact. A common situation is when you have a private cloud for some of your work, but also have a public cloud account for the rest. Each of these clouds require different authentication endpoints, and may require different settings for other things such as region, identity type, etc.

Each envrironment is a separate section in the configuration file, and the section name is used as the name of the envrironment. You can name your environments whatever makes sense to you, but there are two special names: '**default**' and '**settings**'. If a section is named 'default', it is used by pyrax unless you explicitly set a different environment. Also, for backwards compatibility with versions of pyrax before 1.4, a section named 'settings' is interpreted as the default. Those versions only supported a single environment in the configuration file, and used 'settings' as the section name. **NOTE**: if you do not have a section named either 'default' or 'settings', then the first section listed is used as the default environment.
Each environment is a separate section in the configuration file, and the section name is used as the name of the environment. You can name your environments whatever makes sense to you, but there are two special names: '**default**' and '**settings**'. If a section is named 'default', it is used by pyrax unless you explicitly set a different environment. Also, for backwards compatibility with versions of pyrax before 1.4, a section named 'settings' is interpreted as the default. Those versions only supported a single environment in the configuration file, and used 'settings' as the section name. **NOTE**: if you do not have a section named either 'default' or 'settings', then the first section listed is used as the default environment.

### Changing Environments
If you have multiple environments, you need to set the desired envrironment before you authenticate and connect to the services. If you want the `default` environment, you don't need to do anything. But if you want to connect to a different provider, you should run the following:
If you have multiple environments, you need to set the desired environment before you authenticate and connect to the services. If you want the `default` environment, you don't need to do anything. But if you want to connect to a different provider, you should run the following:

import pyrax
pyrax.set_environment("desired_environment")

Note that changing the environment requires that you authenticate against the new envrironment, and create new connections to the various services. In other words, if you had already authenticated so that a service such as `pyrax.cloudservers` referenced the compute service on that cloud, changing the environment to point to a different cloud discards the previous identity and service connections, so that now `pyrax.cloudservers` is `None`. Once you authenticate in the new environment, `pyrax.cloudservers` references the compute service on the cloud for the new environment.
Note that changing the environment requires that you authenticate against the new environment, and create new connections to the various services. In other words, if you had already authenticated so that a service such as `pyrax.cloudservers` referenced the compute service on that cloud, changing the environment to point to a different cloud discards the previous identity and service connections, so that now `pyrax.cloudservers` is `None`. Once you authenticate in the new environment, `pyrax.cloudservers` references the compute service on the cloud for the new environment.


### Available Configuration Settings
Expand Down Expand Up @@ -146,7 +155,7 @@ Here is a sample:

The above configuration file defines two environments: **private** and **public**. Since there is no 'default' or 'settings' section, the 'private' environment is the default, since it is listed first.

When using the 'private' envrironment, pyrax uses Keystone authentication with the tenant name of 'demo', the tenant ID of 'abc123456', and the password stored in the keyring for user 'demo'. It also emits debugging messages for all HTTP requests and responses, and each request contains the standard `User-agent` header of 'pyrax/1.4.x'.
When using the 'private' environment, pyrax uses Keystone authentication with the tenant name of 'demo', the tenant ID of 'abc123456', and the password stored in the keyring for user 'demo'. It also emits debugging messages for all HTTP requests and responses, and each request contains the standard `User-agent` header of 'pyrax/1.4.x'.

If the environment is then changed to 'public', pyrax switches to Rackspace authentication against the ORD region, using the username 'joeracker'. It no longer emits debug messages, and all requests have the custom `User-agent` header of 'CrazyApp/2.0 pyrax/1.4.x'.

Expand Down
50 changes: 29 additions & 21 deletions pyrax/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,29 @@
services = tuple()


def _import_identity(import_str):
full_str = "pyrax.identity.%s" % import_str
return utils.import_class(full_str)



class Settings(object):
"""
Holds and manages the settings for pyrax.
"""
_environment = None
_settings = {}
env_dct = {
"identity_type": "CLOUD_ID_TYPE",
"auth_endpoint": "CLOUD_AUTH_ENDPOINT",
"keyring_username": "CLOUD_KEYRING_USER",
"region": "CLOUD_REGION",
"tenant_id": "CLOUD_TENANT_ID",
"tenant_name": "CLOUD_TENANT_NAME",
"encoding": "CLOUD_ENCODING",
"custom_user_agent": "CLOUD_USER_AGENT",
"debug": "CLOUD_DEBUG",
}
_settings = {"default": dict.fromkeys(env_dct.keys())}


def get(self, key, env=None):
Expand All @@ -137,18 +153,7 @@ def get(self, key, env=None):
return self._settings[env][key]
except KeyError:
# See if it's set in the environment
env_dct = {
"identity_type": "CLOUD_ID_TYPE",
"auth_endpoint": "CLOUD_AUTH_ENDPOINT",
"keyring_username": "CLOUD_KEYRING_USER",
"region": "CLOUD_REGION",
"tenant_id": "CLOUD_TENANT_ID",
"tenant_name": "CLOUD_TENANT_NAME",
"encoding": "CLOUD_ENCODING",
"custom_user_agent": "CLOUD_USER_AGENT",
"debug": "CLOUD_DEBUG",
}
env_var = env_dct.get(key)
env_var = self.env_dct.get(key)
try:
return os.environ[env_var]
except KeyError:
Expand All @@ -166,12 +171,19 @@ def set(self, key, val, env=None):
env = self.environment
else:
if env not in self._settings:
raise EnvironmentNotFound("There is no environment named '%s'."
% env)
raise exc.EnvironmentNotFound("There is no environment named "
"'%s'." % env)
dct = self._settings[env]
if key not in dct:
raise exc.InvalidSetting("The setting '%s' is not defined." % key)
dct[key] = val
# If setting the identity_type, also change the identity_class.
if key == "identity_type":
if val.lower() == "rackspace":
val = "rax_identity.RaxIdentity"
elif val.lower() == "keystone":
val = "keystone_identity.KeystoneIdentity"
dct["identity_class"] = _import_identity(val)


def _getEnvironment(self):
Expand Down Expand Up @@ -219,10 +231,6 @@ def safe_get(section, option, default=None):
except (ConfigParser.NoSectionError, ConfigParser.NoOptionError):
return default

def import_identity(import_str):
full_str = "pyrax.identity.%s" % import_str
return utils.import_class(full_str)

for section in cfg.sections():
if section == "settings":
section_name = "default"
Expand All @@ -237,7 +245,7 @@ def import_identity(import_str):
elif ityp.lower() == "keystone":
ityp = "keystone_identity.KeystoneIdentity"
dct["identity_type"] = ityp
dct["identity_class"] = import_identity(ityp)
dct["identity_class"] = _import_identity(ityp)
# Handle both the old and new names for this setting.
debug = safe_get(section, "debug")
if debug is None:
Expand Down Expand Up @@ -295,7 +303,7 @@ def set_setting(key, val, env=None):
Changes the value of the specified key in the current environment, or in
another environment if specified.
"""
return settings.get(key, val, env=env)
return settings.set(key, val, env=env)


def set_default_region(region):
Expand Down
8 changes: 7 additions & 1 deletion pyrax/identity/rax_identity.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import ConfigParser

from pyrax.base_identity import BaseAuth
from pyrax.base_identity import User
import pyrax.exceptions as exc
Expand All @@ -24,7 +26,11 @@ def _get_auth_endpoint(self):

def _read_credential_file(self, cfg):
self.username = cfg.get("rackspace_cloud", "username")
self.password = cfg.get("rackspace_cloud", "api_key")
try:
self.password = cfg.get("rackspace_cloud", "api_key")
except ConfigParser.NoOptionError as e:
# Allow either the use of either 'api_key' or 'password'.
self.password = cfg.get("rackspace_cloud", "password")


def _get_credentials(self):
Expand Down
2 changes: 1 addition & 1 deletion pyrax/version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-

version = "1.4.1"
version = "1.4.2"
9 changes: 9 additions & 0 deletions tests/unit/test_identity.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,15 @@ def test_set_credential_file(self):
ident.set_credential_file(tmpname)
self.assertEqual(ident.username, user)
self.assertEqual(ident.password, key)
# Using 'password' instead of 'api_key'
with utils.SelfDeletingTempfile() as tmpname:
with open(tmpname, "wb") as ff:
ff.write("[rackspace_cloud]\n")
ff.write("username = %s\n" % user)
ff.write("password = %s\n" % key)
ident.set_credential_file(tmpname)
self.assertEqual(ident.username, user)
self.assertEqual(ident.password, key)
# File doesn't exist
self.assertRaises(exc.FileNotFound, ident.set_credential_file,
"doesn't exist")
Expand Down

0 comments on commit 3705cda

Please sign in to comment.