Author: tomaz
Date: Wed May 22 00:45:33 2013
New Revision: 1485020
URL: http://svn.apache.org/r1485020
Log:
Add ability to re-use existing still valid auth token to the
OpenStackAuthConnection class.
For backward compatibility, preserve the old behavior for now and force re-auth
on every auth() call.
Modified:
libcloud/trunk/libcloud/common/openstack.py
libcloud/trunk/libcloud/test/compute/test_openstack.py
Modified: libcloud/trunk/libcloud/common/openstack.py
URL:
http://svn.apache.org/viewvc/libcloud/trunk/libcloud/common/openstack.py?rev=1485020&r1=1485019&r2=1485020&view=diff
==============================================================================
--- libcloud/trunk/libcloud/common/openstack.py (original)
+++ libcloud/trunk/libcloud/common/openstack.py Wed May 22 00:45:33 2013
@@ -20,6 +20,8 @@ import sys
import binascii
import os
+from datetime import datetime
+
from libcloud.utils.py3 import httplib
from libcloud.utils.iso8601 import parse_date
@@ -34,6 +36,14 @@ except ImportError:
AUTH_API_VERSION = '1.1'
+# Auth versions which contain token expiration information.
+AUTH_VERSIONS_WITH_EXPIRES = [
+ '1.1',
+ '2.0',
+ '2.0_apikey',
+ '2.0_password'
+]
+
__all__ = [
"OpenStackBaseConnection",
"OpenStackAuthConnection"
@@ -110,7 +120,19 @@ class OpenStackAuthConnection(Connection
headers['Content-Type'] = 'application/json; charset=UTF-8'
return headers
- def authenticate(self):
+ def authenticate(self, force=True):
+ """
+ Authenticate against the keystone api.
+
+ @param force: Forcefully update the token even if it's already cached
+ and still valid.
+ @type force: C{bool}
+ """
+ if not force and self.auth_version in AUTH_VERSIONS_WITH_EXPIRES \
+ and self._is_token_valid():
+ # If token is still valid, there is no need to re-authenticate
+ return self
+
if self.auth_version == "1.0":
return self.authenticate_1_0()
elif self.auth_version == "1.1":
@@ -251,6 +273,27 @@ class OpenStackAuthConnection(Connection
return self
+ def _is_token_valid(self):
+ """
+ Return True if the current taken is already cached and hasn't expired
+ yet.
+
+ @rtype: C{bool}
+ """
+ if not self.auth_token:
+ return False
+
+ if not self.auth_token_expires:
+ return False
+
+ time_tuple_expires = self.auth_token_expires.utctimetuple()
+ time_tuple_now = datetime.utcnow().utctimetuple()
+
+ # TODO: Subtract some reasonable grace time period
+ if time_tuple_now < time_tuple_expires:
+ return True
+
+ return False
class OpenStackServiceCatalog(object):
"""
Modified: libcloud/trunk/libcloud/test/compute/test_openstack.py
URL:
http://svn.apache.org/viewvc/libcloud/trunk/libcloud/test/compute/test_openstack.py?rev=1485020&r1=1485019&r2=1485020&view=diff
==============================================================================
--- libcloud/trunk/libcloud/test/compute/test_openstack.py (original)
+++ libcloud/trunk/libcloud/test/compute/test_openstack.py Wed May 22 00:45:33
2013
@@ -12,14 +12,18 @@
# 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.
+
import sys
import unittest
+import datetime
try:
import simplejson as json
except ImportError:
import json
+from mock import Mock
+
from libcloud.utils.py3 import httplib
from libcloud.utils.py3 import method_type
from libcloud.utils.py3 import u
@@ -130,9 +134,58 @@ class OpenStackAuthConnectionTests(unitt
if auth_version in ['2.0', '2.0_apikey', '2.0_password']:
self.assertTrue(osa.auth_user_info is not None)
- def test_token_expiration_and_force_reuath(self):
- # TODO
- pass
+ def test_token_expiration_and_force_reauthentication(self):
+ user_id = OPENSTACK_PARAMS[0]
+ key = OPENSTACK_PARAMS[1]
+
+ connection = self._get_mock_connection(OpenStack_2_0_MockHttp)
+ auth_url = connection.auth_url
+ auth_version = '2.0'
+
+ yesterday = datetime.datetime.today() - datetime.timedelta(1)
+ tomorrow = datetime.datetime.today() + datetime.timedelta(1)
+
+ osa = OpenStackAuthConnection(connection, auth_url, auth_version,
+ user_id, key)
+
+ mocked_auth_method = Mock(wraps=osa.authenticate_2_0_with_body)
+ osa.authenticate_2_0_with_body = mocked_auth_method
+
+ # Force re-auth, expired token
+ osa.auth_token = None
+ osa.auth_token_expires = yesterday
+ count = 5
+
+ for i in range(0, count):
+ osa.authenticate(force=True)
+
+ self.assertEqual(mocked_auth_method.call_count, count)
+
+ # No force reauth, expired token
+ osa.auth_token = None
+ osa.auth_token_expires = yesterday
+
+ mocked_auth_method.call_count = 0
+ self.assertEqual(mocked_auth_method.call_count, 0)
+
+ for i in range(0, count):
+ osa.authenticate(force=False)
+
+ self.assertEqual(mocked_auth_method.call_count, count)
+
+ # No force reauth, valid / non-expired token
+ osa.auth_token = None
+
+ mocked_auth_method.call_count = 0
+ self.assertEqual(mocked_auth_method.call_count, 0)
+
+ for i in range(0, count):
+ osa.authenticate(force=False)
+
+ if i == 0:
+ osa.auth_token_expires = tomorrow
+
+ self.assertEqual(mocked_auth_method.call_count, 1)
def _get_mock_connection(self, mock_http_class):
connection = OpenStackBaseConnection(*OPENSTACK_PARAMS)
@@ -1121,7 +1174,6 @@ class OpenStack_1_1_MockHttp(MockHttpTes
raise NotImplementedError()
-
def _v1_1_slug_servers_12064(self, method, url, body, headers):
if method == "GET":
body = self.fixtures.load('_servers_12064.json')