Merge "Sync oslo-incubator to 1fc3cd47"
This commit is contained in:
		| @@ -1,17 +0,0 @@ | |||||||
| # |  | ||||||
| #    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. |  | ||||||
|  |  | ||||||
| import six |  | ||||||
|  |  | ||||||
|  |  | ||||||
| six.add_move(six.MovedModule('mox', 'mox', 'mox3.mox')) |  | ||||||
|   | |||||||
							
								
								
									
										40
									
								
								keystoneclient/openstack/common/_i18n.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								keystoneclient/openstack/common/_i18n.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | |||||||
|  | #    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. | ||||||
|  |  | ||||||
|  | """oslo.i18n integration module. | ||||||
|  |  | ||||||
|  | See http://docs.openstack.org/developer/oslo.i18n/usage.html | ||||||
|  |  | ||||||
|  | """ | ||||||
|  |  | ||||||
|  | import oslo.i18n | ||||||
|  |  | ||||||
|  |  | ||||||
|  | # NOTE(dhellmann): This reference to o-s-l-o will be replaced by the | ||||||
|  | # application name when this module is synced into the separate | ||||||
|  | # repository. It is OK to have more than one translation function | ||||||
|  | # using the same domain, since there will still only be one message | ||||||
|  | # catalog. | ||||||
|  | _translators = oslo.i18n.TranslatorFactory(domain='keystoneclient') | ||||||
|  |  | ||||||
|  | # The primary translation function using the well-known name "_" | ||||||
|  | _ = _translators.primary | ||||||
|  |  | ||||||
|  | # Translators for log levels. | ||||||
|  | # | ||||||
|  | # The abbreviated names are meant to reflect the usual use of a short | ||||||
|  | # name like '_'. The "L" is for "log" and the other letter comes from | ||||||
|  | # the level. | ||||||
|  | _LI = _translators.log_info | ||||||
|  | _LW = _translators.log_warning | ||||||
|  | _LE = _translators.log_error | ||||||
|  | _LC = _translators.log_critical | ||||||
| @@ -26,12 +26,12 @@ Base utilities to build API operation managers and objects on top of. | |||||||
| import abc | import abc | ||||||
| import copy | import copy | ||||||
|  |  | ||||||
|  | from oslo.utils import strutils | ||||||
| import six | import six | ||||||
| from six.moves.urllib import parse | from six.moves.urllib import parse | ||||||
|  |  | ||||||
|  | from keystoneclient.openstack.common._i18n import _ | ||||||
| from keystoneclient.openstack.common.apiclient import exceptions | from keystoneclient.openstack.common.apiclient import exceptions | ||||||
| from keystoneclient.openstack.common.gettextutils import _ |  | ||||||
| from keystoneclient.openstack.common import strutils |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def getid(obj): | def getid(obj): | ||||||
| @@ -495,6 +495,8 @@ class Resource(object): | |||||||
|         new = self.manager.get(self.id) |         new = self.manager.get(self.id) | ||||||
|         if new: |         if new: | ||||||
|             self._add_details(new._info) |             self._add_details(new._info) | ||||||
|  |             self._add_details( | ||||||
|  |                 {'x_request_id': self.manager.client.last_request_id}) | ||||||
|  |  | ||||||
|     def __eq__(self, other): |     def __eq__(self, other): | ||||||
|         if not isinstance(other, Resource): |         if not isinstance(other, Resource): | ||||||
|   | |||||||
| @@ -25,6 +25,7 @@ OpenStack Client interface. Handles the REST calls and responses. | |||||||
| # E0202: An attribute inherited from %s hide this method | # E0202: An attribute inherited from %s hide this method | ||||||
| # pylint: disable=E0202 | # pylint: disable=E0202 | ||||||
|  |  | ||||||
|  | import hashlib | ||||||
| import logging | import logging | ||||||
| import time | import time | ||||||
|  |  | ||||||
| @@ -33,14 +34,15 @@ try: | |||||||
| except ImportError: | except ImportError: | ||||||
|     import json |     import json | ||||||
|  |  | ||||||
|  | from oslo.utils import encodeutils | ||||||
|  | from oslo.utils import importutils | ||||||
| import requests | import requests | ||||||
|  |  | ||||||
|  | from keystoneclient.openstack.common._i18n import _ | ||||||
| from keystoneclient.openstack.common.apiclient import exceptions | from keystoneclient.openstack.common.apiclient import exceptions | ||||||
| from keystoneclient.openstack.common.gettextutils import _ |  | ||||||
| from keystoneclient.openstack.common import importutils |  | ||||||
|  |  | ||||||
|  |  | ||||||
| _logger = logging.getLogger(__name__) | _logger = logging.getLogger(__name__) | ||||||
|  | SENSITIVE_HEADERS = ('X-Auth-Token', 'X-Subject-Token',) | ||||||
|  |  | ||||||
|  |  | ||||||
| class HTTPClient(object): | class HTTPClient(object): | ||||||
| @@ -98,6 +100,18 @@ class HTTPClient(object): | |||||||
|         self.http = http or requests.Session() |         self.http = http or requests.Session() | ||||||
|  |  | ||||||
|         self.cached_token = None |         self.cached_token = None | ||||||
|  |         self.last_request_id = None | ||||||
|  |  | ||||||
|  |     def _safe_header(self, name, value): | ||||||
|  |         if name in SENSITIVE_HEADERS: | ||||||
|  |             # because in python3 byte string handling is ... ug | ||||||
|  |             v = value.encode('utf-8') | ||||||
|  |             h = hashlib.sha1(v) | ||||||
|  |             d = h.hexdigest() | ||||||
|  |             return encodeutils.safe_decode(name), "{SHA1}%s" % d | ||||||
|  |         else: | ||||||
|  |             return (encodeutils.safe_decode(name), | ||||||
|  |                     encodeutils.safe_decode(value)) | ||||||
|  |  | ||||||
|     def _http_log_req(self, method, url, kwargs): |     def _http_log_req(self, method, url, kwargs): | ||||||
|         if not self.debug: |         if not self.debug: | ||||||
| @@ -110,7 +124,8 @@ class HTTPClient(object): | |||||||
|         ] |         ] | ||||||
|  |  | ||||||
|         for element in kwargs['headers']: |         for element in kwargs['headers']: | ||||||
|             header = "-H '%s: %s'" % (element, kwargs['headers'][element]) |             header = ("-H '%s: %s'" % | ||||||
|  |                       self._safe_header(element, kwargs['headers'][element])) | ||||||
|             string_parts.append(header) |             string_parts.append(header) | ||||||
|  |  | ||||||
|         _logger.debug("REQ: %s" % " ".join(string_parts)) |         _logger.debug("REQ: %s" % " ".join(string_parts)) | ||||||
| @@ -156,7 +171,7 @@ class HTTPClient(object): | |||||||
|              requests.Session.request (such as `headers`) or `json` |              requests.Session.request (such as `headers`) or `json` | ||||||
|              that will be encoded as JSON and used as `data` argument |              that will be encoded as JSON and used as `data` argument | ||||||
|         """ |         """ | ||||||
|         kwargs.setdefault("headers", kwargs.get("headers", {})) |         kwargs.setdefault("headers", {}) | ||||||
|         kwargs["headers"]["User-Agent"] = self.user_agent |         kwargs["headers"]["User-Agent"] = self.user_agent | ||||||
|         if self.original_ip: |         if self.original_ip: | ||||||
|             kwargs["headers"]["Forwarded"] = "for=%s;by=%s" % ( |             kwargs["headers"]["Forwarded"] = "for=%s;by=%s" % ( | ||||||
| @@ -177,6 +192,8 @@ class HTTPClient(object): | |||||||
|                                start_time, time.time())) |                                start_time, time.time())) | ||||||
|         self._http_log_resp(resp) |         self._http_log_resp(resp) | ||||||
|  |  | ||||||
|  |         self.last_request_id = resp.headers.get('x-openstack-request-id') | ||||||
|  |  | ||||||
|         if resp.status_code >= 400: |         if resp.status_code >= 400: | ||||||
|             _logger.debug( |             _logger.debug( | ||||||
|                 "Request returned failure status: %s", |                 "Request returned failure status: %s", | ||||||
| @@ -247,6 +264,10 @@ class HTTPClient(object): | |||||||
|                 raise |                 raise | ||||||
|             self.cached_token = None |             self.cached_token = None | ||||||
|             client.cached_endpoint = None |             client.cached_endpoint = None | ||||||
|  |             if self.auth_plugin.opts.get('token'): | ||||||
|  |                 self.auth_plugin.opts['token'] = None | ||||||
|  |             if self.auth_plugin.opts.get('endpoint'): | ||||||
|  |                 self.auth_plugin.opts['endpoint'] = None | ||||||
|             self.authenticate() |             self.authenticate() | ||||||
|             try: |             try: | ||||||
|                 token, endpoint = self.auth_plugin.token_and_endpoint( |                 token, endpoint = self.auth_plugin.token_and_endpoint( | ||||||
| @@ -323,6 +344,10 @@ class BaseClient(object): | |||||||
|         return self.http_client.client_request( |         return self.http_client.client_request( | ||||||
|             self, method, url, **kwargs) |             self, method, url, **kwargs) | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def last_request_id(self): | ||||||
|  |         return self.http_client.last_request_id | ||||||
|  |  | ||||||
|     def head(self, url, **kwargs): |     def head(self, url, **kwargs): | ||||||
|         return self.client_request("HEAD", url, **kwargs) |         return self.client_request("HEAD", url, **kwargs) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -25,7 +25,7 @@ import sys | |||||||
|  |  | ||||||
| import six | import six | ||||||
|  |  | ||||||
| from keystoneclient.openstack.common.gettextutils import _ | from keystoneclient.openstack.common._i18n import _ | ||||||
|  |  | ||||||
|  |  | ||||||
| class ClientException(Exception): | class ClientException(Exception): | ||||||
| @@ -34,14 +34,6 @@ class ClientException(Exception): | |||||||
|     pass |     pass | ||||||
|  |  | ||||||
|  |  | ||||||
| class MissingArgs(ClientException): |  | ||||||
|     """Supplied arguments are not sufficient for calling a function.""" |  | ||||||
|     def __init__(self, missing): |  | ||||||
|         self.missing = missing |  | ||||||
|         msg = _("Missing arguments: %s") % ", ".join(missing) |  | ||||||
|         super(MissingArgs, self).__init__(msg) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class ValidationError(ClientException): | class ValidationError(ClientException): | ||||||
|     """Error in validation on API client side.""" |     """Error in validation on API client side.""" | ||||||
|     pass |     pass | ||||||
|   | |||||||
| @@ -168,6 +168,8 @@ class FakeHTTPClient(client.HTTPClient): | |||||||
|         else: |         else: | ||||||
|             status, body = resp |             status, body = resp | ||||||
|             headers = {} |             headers = {} | ||||||
|  |         self.last_request_id = headers.get('x-openstack-request-id', | ||||||
|  |                                            'req-test') | ||||||
|         return TestResponse({ |         return TestResponse({ | ||||||
|             "status_code": status, |             "status_code": status, | ||||||
|             "text": body, |             "text": body, | ||||||
|   | |||||||
							
								
								
									
										87
									
								
								keystoneclient/openstack/common/apiclient/utils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								keystoneclient/openstack/common/apiclient/utils.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | |||||||
|  | # | ||||||
|  | #    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. | ||||||
|  |  | ||||||
|  | from oslo.utils import encodeutils | ||||||
|  | import six | ||||||
|  |  | ||||||
|  | from keystoneclient.openstack.common._i18n import _ | ||||||
|  | from keystoneclient.openstack.common.apiclient import exceptions | ||||||
|  | from keystoneclient.openstack.common import uuidutils | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def find_resource(manager, name_or_id, **find_args): | ||||||
|  |     """Look for resource in a given manager. | ||||||
|  |  | ||||||
|  |     Used as a helper for the _find_* methods. | ||||||
|  |     Example: | ||||||
|  |  | ||||||
|  |     .. code-block:: python | ||||||
|  |  | ||||||
|  |         def _find_hypervisor(cs, hypervisor): | ||||||
|  |             #Get a hypervisor by name or ID. | ||||||
|  |             return cliutils.find_resource(cs.hypervisors, hypervisor) | ||||||
|  |     """ | ||||||
|  |     # first try to get entity as integer id | ||||||
|  |     try: | ||||||
|  |         return manager.get(int(name_or_id)) | ||||||
|  |     except (TypeError, ValueError, exceptions.NotFound): | ||||||
|  |         pass | ||||||
|  |  | ||||||
|  |     # now try to get entity as uuid | ||||||
|  |     try: | ||||||
|  |         if six.PY2: | ||||||
|  |             tmp_id = encodeutils.safe_encode(name_or_id) | ||||||
|  |         else: | ||||||
|  |             tmp_id = encodeutils.safe_decode(name_or_id) | ||||||
|  |  | ||||||
|  |         if uuidutils.is_uuid_like(tmp_id): | ||||||
|  |             return manager.get(tmp_id) | ||||||
|  |     except (TypeError, ValueError, exceptions.NotFound): | ||||||
|  |         pass | ||||||
|  |  | ||||||
|  |     # for str id which is not uuid | ||||||
|  |     if getattr(manager, 'is_alphanum_id_allowed', False): | ||||||
|  |         try: | ||||||
|  |             return manager.get(name_or_id) | ||||||
|  |         except exceptions.NotFound: | ||||||
|  |             pass | ||||||
|  |  | ||||||
|  |     try: | ||||||
|  |         try: | ||||||
|  |             return manager.find(human_id=name_or_id, **find_args) | ||||||
|  |         except exceptions.NotFound: | ||||||
|  |             pass | ||||||
|  |  | ||||||
|  |         # finally try to find entity by name | ||||||
|  |         try: | ||||||
|  |             resource = getattr(manager, 'resource_class', None) | ||||||
|  |             name_attr = resource.NAME_ATTR if resource else 'name' | ||||||
|  |             kwargs = {name_attr: name_or_id} | ||||||
|  |             kwargs.update(find_args) | ||||||
|  |             return manager.find(**kwargs) | ||||||
|  |         except exceptions.NotFound: | ||||||
|  |             msg = _("No %(name)s with a name or " | ||||||
|  |                     "ID of '%(name_or_id)s' exists.") % \ | ||||||
|  |                 { | ||||||
|  |                     "name": manager.resource_class.__name__.lower(), | ||||||
|  |                     "name_or_id": name_or_id | ||||||
|  |                 } | ||||||
|  |             raise exceptions.CommandError(msg) | ||||||
|  |     except exceptions.NoUniqueMatch: | ||||||
|  |         msg = _("Multiple %(name)s matches found for " | ||||||
|  |                 "'%(name_or_id)s', use an ID to be more specific.") % \ | ||||||
|  |             { | ||||||
|  |                 "name": manager.resource_class.__name__.lower(), | ||||||
|  |                 "name_or_id": name_or_id | ||||||
|  |             } | ||||||
|  |         raise exceptions.CommandError(msg) | ||||||
| @@ -1,479 +0,0 @@ | |||||||
| # Copyright 2012 Red Hat, Inc. |  | ||||||
| # Copyright 2013 IBM Corp. |  | ||||||
| # All Rights Reserved. |  | ||||||
| # |  | ||||||
| #    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. |  | ||||||
|  |  | ||||||
| """ |  | ||||||
| gettext for openstack-common modules. |  | ||||||
|  |  | ||||||
| Usual usage in an openstack.common module: |  | ||||||
|  |  | ||||||
|     from keystoneclient.openstack.common.gettextutils import _ |  | ||||||
| """ |  | ||||||
|  |  | ||||||
| import copy |  | ||||||
| import gettext |  | ||||||
| import locale |  | ||||||
| from logging import handlers |  | ||||||
| import os |  | ||||||
|  |  | ||||||
| from babel import localedata |  | ||||||
| import six |  | ||||||
|  |  | ||||||
| _AVAILABLE_LANGUAGES = {} |  | ||||||
|  |  | ||||||
| # FIXME(dhellmann): Remove this when moving to oslo.i18n. |  | ||||||
| USE_LAZY = False |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class TranslatorFactory(object): |  | ||||||
|     """Create translator functions |  | ||||||
|     """ |  | ||||||
|  |  | ||||||
|     def __init__(self, domain, localedir=None): |  | ||||||
|         """Establish a set of translation functions for the domain. |  | ||||||
|  |  | ||||||
|         :param domain: Name of translation domain, |  | ||||||
|                        specifying a message catalog. |  | ||||||
|         :type domain: str |  | ||||||
|         :param lazy: Delays translation until a message is emitted. |  | ||||||
|                      Defaults to False. |  | ||||||
|         :type lazy: Boolean |  | ||||||
|         :param localedir: Directory with translation catalogs. |  | ||||||
|         :type localedir: str |  | ||||||
|         """ |  | ||||||
|         self.domain = domain |  | ||||||
|         if localedir is None: |  | ||||||
|             localedir = os.environ.get(domain.upper() + '_LOCALEDIR') |  | ||||||
|         self.localedir = localedir |  | ||||||
|  |  | ||||||
|     def _make_translation_func(self, domain=None): |  | ||||||
|         """Return a new translation function ready for use. |  | ||||||
|  |  | ||||||
|         Takes into account whether or not lazy translation is being |  | ||||||
|         done. |  | ||||||
|  |  | ||||||
|         The domain can be specified to override the default from the |  | ||||||
|         factory, but the localedir from the factory is always used |  | ||||||
|         because we assume the log-level translation catalogs are |  | ||||||
|         installed in the same directory as the main application |  | ||||||
|         catalog. |  | ||||||
|  |  | ||||||
|         """ |  | ||||||
|         if domain is None: |  | ||||||
|             domain = self.domain |  | ||||||
|         t = gettext.translation(domain, |  | ||||||
|                                 localedir=self.localedir, |  | ||||||
|                                 fallback=True) |  | ||||||
|         # Use the appropriate method of the translation object based |  | ||||||
|         # on the python version. |  | ||||||
|         m = t.gettext if six.PY3 else t.ugettext |  | ||||||
|  |  | ||||||
|         def f(msg): |  | ||||||
|             """oslo.i18n.gettextutils translation function.""" |  | ||||||
|             if USE_LAZY: |  | ||||||
|                 return Message(msg, domain=domain) |  | ||||||
|             return m(msg) |  | ||||||
|         return f |  | ||||||
|  |  | ||||||
|     @property |  | ||||||
|     def primary(self): |  | ||||||
|         "The default translation function." |  | ||||||
|         return self._make_translation_func() |  | ||||||
|  |  | ||||||
|     def _make_log_translation_func(self, level): |  | ||||||
|         return self._make_translation_func(self.domain + '-log-' + level) |  | ||||||
|  |  | ||||||
|     @property |  | ||||||
|     def log_info(self): |  | ||||||
|         "Translate info-level log messages." |  | ||||||
|         return self._make_log_translation_func('info') |  | ||||||
|  |  | ||||||
|     @property |  | ||||||
|     def log_warning(self): |  | ||||||
|         "Translate warning-level log messages." |  | ||||||
|         return self._make_log_translation_func('warning') |  | ||||||
|  |  | ||||||
|     @property |  | ||||||
|     def log_error(self): |  | ||||||
|         "Translate error-level log messages." |  | ||||||
|         return self._make_log_translation_func('error') |  | ||||||
|  |  | ||||||
|     @property |  | ||||||
|     def log_critical(self): |  | ||||||
|         "Translate critical-level log messages." |  | ||||||
|         return self._make_log_translation_func('critical') |  | ||||||
|  |  | ||||||
|  |  | ||||||
| # NOTE(dhellmann): When this module moves out of the incubator into |  | ||||||
| # oslo.i18n, these global variables can be moved to an integration |  | ||||||
| # module within each application. |  | ||||||
|  |  | ||||||
| # Create the global translation functions. |  | ||||||
| _translators = TranslatorFactory('keystoneclient') |  | ||||||
|  |  | ||||||
| # The primary translation function using the well-known name "_" |  | ||||||
| _ = _translators.primary |  | ||||||
|  |  | ||||||
| # Translators for log levels. |  | ||||||
| # |  | ||||||
| # The abbreviated names are meant to reflect the usual use of a short |  | ||||||
| # name like '_'. The "L" is for "log" and the other letter comes from |  | ||||||
| # the level. |  | ||||||
| _LI = _translators.log_info |  | ||||||
| _LW = _translators.log_warning |  | ||||||
| _LE = _translators.log_error |  | ||||||
| _LC = _translators.log_critical |  | ||||||
|  |  | ||||||
| # NOTE(dhellmann): End of globals that will move to the application's |  | ||||||
| # integration module. |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def enable_lazy(): |  | ||||||
|     """Convenience function for configuring _() to use lazy gettext |  | ||||||
|  |  | ||||||
|     Call this at the start of execution to enable the gettextutils._ |  | ||||||
|     function to use lazy gettext functionality. This is useful if |  | ||||||
|     your project is importing _ directly instead of using the |  | ||||||
|     gettextutils.install() way of importing the _ function. |  | ||||||
|     """ |  | ||||||
|     global USE_LAZY |  | ||||||
|     USE_LAZY = True |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def install(domain): |  | ||||||
|     """Install a _() function using the given translation domain. |  | ||||||
|  |  | ||||||
|     Given a translation domain, install a _() function using gettext's |  | ||||||
|     install() function. |  | ||||||
|  |  | ||||||
|     The main difference from gettext.install() is that we allow |  | ||||||
|     overriding the default localedir (e.g. /usr/share/locale) using |  | ||||||
|     a translation-domain-specific environment variable (e.g. |  | ||||||
|     NOVA_LOCALEDIR). |  | ||||||
|  |  | ||||||
|     Note that to enable lazy translation, enable_lazy must be |  | ||||||
|     called. |  | ||||||
|  |  | ||||||
|     :param domain: the translation domain |  | ||||||
|     """ |  | ||||||
|     from six import moves |  | ||||||
|     tf = TranslatorFactory(domain) |  | ||||||
|     moves.builtins.__dict__['_'] = tf.primary |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class Message(six.text_type): |  | ||||||
|     """A Message object is a unicode object that can be translated. |  | ||||||
|  |  | ||||||
|     Translation of Message is done explicitly using the translate() method. |  | ||||||
|     For all non-translation intents and purposes, a Message is simply unicode, |  | ||||||
|     and can be treated as such. |  | ||||||
|     """ |  | ||||||
|  |  | ||||||
|     def __new__(cls, msgid, msgtext=None, params=None, |  | ||||||
|                 domain='keystoneclient', *args): |  | ||||||
|         """Create a new Message object. |  | ||||||
|  |  | ||||||
|         In order for translation to work gettext requires a message ID, this |  | ||||||
|         msgid will be used as the base unicode text. It is also possible |  | ||||||
|         for the msgid and the base unicode text to be different by passing |  | ||||||
|         the msgtext parameter. |  | ||||||
|         """ |  | ||||||
|         # If the base msgtext is not given, we use the default translation |  | ||||||
|         # of the msgid (which is in English) just in case the system locale is |  | ||||||
|         # not English, so that the base text will be in that locale by default. |  | ||||||
|         if not msgtext: |  | ||||||
|             msgtext = Message._translate_msgid(msgid, domain) |  | ||||||
|         # We want to initialize the parent unicode with the actual object that |  | ||||||
|         # would have been plain unicode if 'Message' was not enabled. |  | ||||||
|         msg = super(Message, cls).__new__(cls, msgtext) |  | ||||||
|         msg.msgid = msgid |  | ||||||
|         msg.domain = domain |  | ||||||
|         msg.params = params |  | ||||||
|         return msg |  | ||||||
|  |  | ||||||
|     def translate(self, desired_locale=None): |  | ||||||
|         """Translate this message to the desired locale. |  | ||||||
|  |  | ||||||
|         :param desired_locale: The desired locale to translate the message to, |  | ||||||
|                                if no locale is provided the message will be |  | ||||||
|                                translated to the system's default locale. |  | ||||||
|  |  | ||||||
|         :returns: the translated message in unicode |  | ||||||
|         """ |  | ||||||
|  |  | ||||||
|         translated_message = Message._translate_msgid(self.msgid, |  | ||||||
|                                                       self.domain, |  | ||||||
|                                                       desired_locale) |  | ||||||
|         if self.params is None: |  | ||||||
|             # No need for more translation |  | ||||||
|             return translated_message |  | ||||||
|  |  | ||||||
|         # This Message object may have been formatted with one or more |  | ||||||
|         # Message objects as substitution arguments, given either as a single |  | ||||||
|         # argument, part of a tuple, or as one or more values in a dictionary. |  | ||||||
|         # When translating this Message we need to translate those Messages too |  | ||||||
|         translated_params = _translate_args(self.params, desired_locale) |  | ||||||
|  |  | ||||||
|         translated_message = translated_message % translated_params |  | ||||||
|  |  | ||||||
|         return translated_message |  | ||||||
|  |  | ||||||
|     @staticmethod |  | ||||||
|     def _translate_msgid(msgid, domain, desired_locale=None): |  | ||||||
|         if not desired_locale: |  | ||||||
|             system_locale = locale.getdefaultlocale() |  | ||||||
|             # If the system locale is not available to the runtime use English |  | ||||||
|             if not system_locale[0]: |  | ||||||
|                 desired_locale = 'en_US' |  | ||||||
|             else: |  | ||||||
|                 desired_locale = system_locale[0] |  | ||||||
|  |  | ||||||
|         locale_dir = os.environ.get(domain.upper() + '_LOCALEDIR') |  | ||||||
|         lang = gettext.translation(domain, |  | ||||||
|                                    localedir=locale_dir, |  | ||||||
|                                    languages=[desired_locale], |  | ||||||
|                                    fallback=True) |  | ||||||
|         if six.PY3: |  | ||||||
|             translator = lang.gettext |  | ||||||
|         else: |  | ||||||
|             translator = lang.ugettext |  | ||||||
|  |  | ||||||
|         translated_message = translator(msgid) |  | ||||||
|         return translated_message |  | ||||||
|  |  | ||||||
|     def __mod__(self, other): |  | ||||||
|         # When we mod a Message we want the actual operation to be performed |  | ||||||
|         # by the parent class (i.e. unicode()), the only thing  we do here is |  | ||||||
|         # save the original msgid and the parameters in case of a translation |  | ||||||
|         params = self._sanitize_mod_params(other) |  | ||||||
|         unicode_mod = super(Message, self).__mod__(params) |  | ||||||
|         modded = Message(self.msgid, |  | ||||||
|                          msgtext=unicode_mod, |  | ||||||
|                          params=params, |  | ||||||
|                          domain=self.domain) |  | ||||||
|         return modded |  | ||||||
|  |  | ||||||
|     def _sanitize_mod_params(self, other): |  | ||||||
|         """Sanitize the object being modded with this Message. |  | ||||||
|  |  | ||||||
|         - Add support for modding 'None' so translation supports it |  | ||||||
|         - Trim the modded object, which can be a large dictionary, to only |  | ||||||
|         those keys that would actually be used in a translation |  | ||||||
|         - Snapshot the object being modded, in case the message is |  | ||||||
|         translated, it will be used as it was when the Message was created |  | ||||||
|         """ |  | ||||||
|         if other is None: |  | ||||||
|             params = (other,) |  | ||||||
|         elif isinstance(other, dict): |  | ||||||
|             # Merge the dictionaries |  | ||||||
|             # Copy each item in case one does not support deep copy. |  | ||||||
|             params = {} |  | ||||||
|             if isinstance(self.params, dict): |  | ||||||
|                 for key, val in self.params.items(): |  | ||||||
|                     params[key] = self._copy_param(val) |  | ||||||
|             for key, val in other.items(): |  | ||||||
|                 params[key] = self._copy_param(val) |  | ||||||
|         else: |  | ||||||
|             params = self._copy_param(other) |  | ||||||
|         return params |  | ||||||
|  |  | ||||||
|     def _copy_param(self, param): |  | ||||||
|         try: |  | ||||||
|             return copy.deepcopy(param) |  | ||||||
|         except Exception: |  | ||||||
|             # Fallback to casting to unicode this will handle the |  | ||||||
|             # python code-like objects that can't be deep-copied |  | ||||||
|             return six.text_type(param) |  | ||||||
|  |  | ||||||
|     def __add__(self, other): |  | ||||||
|         msg = _('Message objects do not support addition.') |  | ||||||
|         raise TypeError(msg) |  | ||||||
|  |  | ||||||
|     def __radd__(self, other): |  | ||||||
|         return self.__add__(other) |  | ||||||
|  |  | ||||||
|     if six.PY2: |  | ||||||
|         def __str__(self): |  | ||||||
|             # NOTE(luisg): Logging in python 2.6 tries to str() log records, |  | ||||||
|             # and it expects specifically a UnicodeError in order to proceed. |  | ||||||
|             msg = _('Message objects do not support str() because they may ' |  | ||||||
|                     'contain non-ascii characters. ' |  | ||||||
|                     'Please use unicode() or translate() instead.') |  | ||||||
|             raise UnicodeError(msg) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def get_available_languages(domain): |  | ||||||
|     """Lists the available languages for the given translation domain. |  | ||||||
|  |  | ||||||
|     :param domain: the domain to get languages for |  | ||||||
|     """ |  | ||||||
|     if domain in _AVAILABLE_LANGUAGES: |  | ||||||
|         return copy.copy(_AVAILABLE_LANGUAGES[domain]) |  | ||||||
|  |  | ||||||
|     localedir = '%s_LOCALEDIR' % domain.upper() |  | ||||||
|     find = lambda x: gettext.find(domain, |  | ||||||
|                                   localedir=os.environ.get(localedir), |  | ||||||
|                                   languages=[x]) |  | ||||||
|  |  | ||||||
|     # NOTE(mrodden): en_US should always be available (and first in case |  | ||||||
|     # order matters) since our in-line message strings are en_US |  | ||||||
|     language_list = ['en_US'] |  | ||||||
|     # NOTE(luisg): Babel <1.0 used a function called list(), which was |  | ||||||
|     # renamed to locale_identifiers() in >=1.0, the requirements master list |  | ||||||
|     # requires >=0.9.6, uncapped, so defensively work with both. We can remove |  | ||||||
|     # this check when the master list updates to >=1.0, and update all projects |  | ||||||
|     list_identifiers = (getattr(localedata, 'list', None) or |  | ||||||
|                         getattr(localedata, 'locale_identifiers')) |  | ||||||
|     locale_identifiers = list_identifiers() |  | ||||||
|  |  | ||||||
|     for i in locale_identifiers: |  | ||||||
|         if find(i) is not None: |  | ||||||
|             language_list.append(i) |  | ||||||
|  |  | ||||||
|     # NOTE(luisg): Babel>=1.0,<1.3 has a bug where some OpenStack supported |  | ||||||
|     # locales (e.g. 'zh_CN', and 'zh_TW') aren't supported even though they |  | ||||||
|     # are perfectly legitimate locales: |  | ||||||
|     #     https://github.com/mitsuhiko/babel/issues/37 |  | ||||||
|     # In Babel 1.3 they fixed the bug and they support these locales, but |  | ||||||
|     # they are still not explicitly "listed" by locale_identifiers(). |  | ||||||
|     # That is  why we add the locales here explicitly if necessary so that |  | ||||||
|     # they are listed as supported. |  | ||||||
|     aliases = {'zh': 'zh_CN', |  | ||||||
|                'zh_Hant_HK': 'zh_HK', |  | ||||||
|                'zh_Hant': 'zh_TW', |  | ||||||
|                'fil': 'tl_PH'} |  | ||||||
|     for (locale_, alias) in six.iteritems(aliases): |  | ||||||
|         if locale_ in language_list and alias not in language_list: |  | ||||||
|             language_list.append(alias) |  | ||||||
|  |  | ||||||
|     _AVAILABLE_LANGUAGES[domain] = language_list |  | ||||||
|     return copy.copy(language_list) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def translate(obj, desired_locale=None): |  | ||||||
|     """Gets the translated unicode representation of the given object. |  | ||||||
|  |  | ||||||
|     If the object is not translatable it is returned as-is. |  | ||||||
|     If the locale is None the object is translated to the system locale. |  | ||||||
|  |  | ||||||
|     :param obj: the object to translate |  | ||||||
|     :param desired_locale: the locale to translate the message to, if None the |  | ||||||
|                            default system locale will be used |  | ||||||
|     :returns: the translated object in unicode, or the original object if |  | ||||||
|               it could not be translated |  | ||||||
|     """ |  | ||||||
|     message = obj |  | ||||||
|     if not isinstance(message, Message): |  | ||||||
|         # If the object to translate is not already translatable, |  | ||||||
|         # let's first get its unicode representation |  | ||||||
|         message = six.text_type(obj) |  | ||||||
|     if isinstance(message, Message): |  | ||||||
|         # Even after unicoding() we still need to check if we are |  | ||||||
|         # running with translatable unicode before translating |  | ||||||
|         return message.translate(desired_locale) |  | ||||||
|     return obj |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def _translate_args(args, desired_locale=None): |  | ||||||
|     """Translates all the translatable elements of the given arguments object. |  | ||||||
|  |  | ||||||
|     This method is used for translating the translatable values in method |  | ||||||
|     arguments which include values of tuples or dictionaries. |  | ||||||
|     If the object is not a tuple or a dictionary the object itself is |  | ||||||
|     translated if it is translatable. |  | ||||||
|  |  | ||||||
|     If the locale is None the object is translated to the system locale. |  | ||||||
|  |  | ||||||
|     :param args: the args to translate |  | ||||||
|     :param desired_locale: the locale to translate the args to, if None the |  | ||||||
|                            default system locale will be used |  | ||||||
|     :returns: a new args object with the translated contents of the original |  | ||||||
|     """ |  | ||||||
|     if isinstance(args, tuple): |  | ||||||
|         return tuple(translate(v, desired_locale) for v in args) |  | ||||||
|     if isinstance(args, dict): |  | ||||||
|         translated_dict = {} |  | ||||||
|         for (k, v) in six.iteritems(args): |  | ||||||
|             translated_v = translate(v, desired_locale) |  | ||||||
|             translated_dict[k] = translated_v |  | ||||||
|         return translated_dict |  | ||||||
|     return translate(args, desired_locale) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class TranslationHandler(handlers.MemoryHandler): |  | ||||||
|     """Handler that translates records before logging them. |  | ||||||
|  |  | ||||||
|     The TranslationHandler takes a locale and a target logging.Handler object |  | ||||||
|     to forward LogRecord objects to after translating them. This handler |  | ||||||
|     depends on Message objects being logged, instead of regular strings. |  | ||||||
|  |  | ||||||
|     The handler can be configured declaratively in the logging.conf as follows: |  | ||||||
|  |  | ||||||
|         [handlers] |  | ||||||
|         keys = translatedlog, translator |  | ||||||
|  |  | ||||||
|         [handler_translatedlog] |  | ||||||
|         class = handlers.WatchedFileHandler |  | ||||||
|         args = ('/var/log/api-localized.log',) |  | ||||||
|         formatter = context |  | ||||||
|  |  | ||||||
|         [handler_translator] |  | ||||||
|         class = openstack.common.log.TranslationHandler |  | ||||||
|         target = translatedlog |  | ||||||
|         args = ('zh_CN',) |  | ||||||
|  |  | ||||||
|     If the specified locale is not available in the system, the handler will |  | ||||||
|     log in the default locale. |  | ||||||
|     """ |  | ||||||
|  |  | ||||||
|     def __init__(self, locale=None, target=None): |  | ||||||
|         """Initialize a TranslationHandler |  | ||||||
|  |  | ||||||
|         :param locale: locale to use for translating messages |  | ||||||
|         :param target: logging.Handler object to forward |  | ||||||
|                        LogRecord objects to after translation |  | ||||||
|         """ |  | ||||||
|         # NOTE(luisg): In order to allow this handler to be a wrapper for |  | ||||||
|         # other handlers, such as a FileHandler, and still be able to |  | ||||||
|         # configure it using logging.conf, this handler has to extend |  | ||||||
|         # MemoryHandler because only the MemoryHandlers' logging.conf |  | ||||||
|         # parsing is implemented such that it accepts a target handler. |  | ||||||
|         handlers.MemoryHandler.__init__(self, capacity=0, target=target) |  | ||||||
|         self.locale = locale |  | ||||||
|  |  | ||||||
|     def setFormatter(self, fmt): |  | ||||||
|         self.target.setFormatter(fmt) |  | ||||||
|  |  | ||||||
|     def emit(self, record): |  | ||||||
|         # We save the message from the original record to restore it |  | ||||||
|         # after translation, so other handlers are not affected by this |  | ||||||
|         original_msg = record.msg |  | ||||||
|         original_args = record.args |  | ||||||
|  |  | ||||||
|         try: |  | ||||||
|             self._translate_and_log_record(record) |  | ||||||
|         finally: |  | ||||||
|             record.msg = original_msg |  | ||||||
|             record.args = original_args |  | ||||||
|  |  | ||||||
|     def _translate_and_log_record(self, record): |  | ||||||
|         record.msg = translate(record.msg, self.locale) |  | ||||||
|  |  | ||||||
|         # In addition to translating the message, we also need to translate |  | ||||||
|         # arguments that were passed to the log method that were not part |  | ||||||
|         # of the main message e.g., log.info(_('Some message %s'), this_one)) |  | ||||||
|         record.args = _translate_args(record.args, self.locale) |  | ||||||
|  |  | ||||||
|         self.target.emit(record) |  | ||||||
| @@ -1,73 +0,0 @@ | |||||||
| # Copyright 2011 OpenStack Foundation. |  | ||||||
| # All Rights Reserved. |  | ||||||
| # |  | ||||||
| #    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. |  | ||||||
|  |  | ||||||
| """ |  | ||||||
| Import related utilities and helper functions. |  | ||||||
| """ |  | ||||||
|  |  | ||||||
| import sys |  | ||||||
| import traceback |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def import_class(import_str): |  | ||||||
|     """Returns a class from a string including module and class.""" |  | ||||||
|     mod_str, _sep, class_str = import_str.rpartition('.') |  | ||||||
|     __import__(mod_str) |  | ||||||
|     try: |  | ||||||
|         return getattr(sys.modules[mod_str], class_str) |  | ||||||
|     except AttributeError: |  | ||||||
|         raise ImportError('Class %s cannot be found (%s)' % |  | ||||||
|                           (class_str, |  | ||||||
|                            traceback.format_exception(*sys.exc_info()))) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def import_object(import_str, *args, **kwargs): |  | ||||||
|     """Import a class and return an instance of it.""" |  | ||||||
|     return import_class(import_str)(*args, **kwargs) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def import_object_ns(name_space, import_str, *args, **kwargs): |  | ||||||
|     """Tries to import object from default namespace. |  | ||||||
|  |  | ||||||
|     Imports a class and return an instance of it, first by trying |  | ||||||
|     to find the class in a default namespace, then failing back to |  | ||||||
|     a full path if not found in the default namespace. |  | ||||||
|     """ |  | ||||||
|     import_value = "%s.%s" % (name_space, import_str) |  | ||||||
|     try: |  | ||||||
|         return import_class(import_value)(*args, **kwargs) |  | ||||||
|     except ImportError: |  | ||||||
|         return import_class(import_str)(*args, **kwargs) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def import_module(import_str): |  | ||||||
|     """Import a module.""" |  | ||||||
|     __import__(import_str) |  | ||||||
|     return sys.modules[import_str] |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def import_versioned_module(version, submodule=None): |  | ||||||
|     module = 'keystoneclient.v%s' % version |  | ||||||
|     if submodule: |  | ||||||
|         module = '.'.join((module, submodule)) |  | ||||||
|     return import_module(module) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def try_import(import_str, default=None): |  | ||||||
|     """Try to import a module and if it fails return default.""" |  | ||||||
|     try: |  | ||||||
|         return import_module(import_str) |  | ||||||
|     except ImportError: |  | ||||||
|         return default |  | ||||||
| @@ -17,8 +17,7 @@ | |||||||
| """Super simple fake memcache client.""" | """Super simple fake memcache client.""" | ||||||
|  |  | ||||||
| from oslo.config import cfg | from oslo.config import cfg | ||||||
|  | from oslo.utils import timeutils | ||||||
| from keystoneclient.openstack.common import timeutils |  | ||||||
|  |  | ||||||
| memcache_opts = [ | memcache_opts = [ | ||||||
|     cfg.ListOpt('memcached_servers', |     cfg.ListOpt('memcached_servers', | ||||||
|   | |||||||
| @@ -1,311 +0,0 @@ | |||||||
| # Copyright 2011 OpenStack Foundation. |  | ||||||
| # All Rights Reserved. |  | ||||||
| # |  | ||||||
| #    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. |  | ||||||
|  |  | ||||||
| """ |  | ||||||
| System-level utilities and helper functions. |  | ||||||
| """ |  | ||||||
|  |  | ||||||
| import math |  | ||||||
| import re |  | ||||||
| import sys |  | ||||||
| import unicodedata |  | ||||||
|  |  | ||||||
| import six |  | ||||||
|  |  | ||||||
| from keystoneclient.openstack.common.gettextutils import _ |  | ||||||
|  |  | ||||||
|  |  | ||||||
| UNIT_PREFIX_EXPONENT = { |  | ||||||
|     'k': 1, |  | ||||||
|     'K': 1, |  | ||||||
|     'Ki': 1, |  | ||||||
|     'M': 2, |  | ||||||
|     'Mi': 2, |  | ||||||
|     'G': 3, |  | ||||||
|     'Gi': 3, |  | ||||||
|     'T': 4, |  | ||||||
|     'Ti': 4, |  | ||||||
| } |  | ||||||
| UNIT_SYSTEM_INFO = { |  | ||||||
|     'IEC': (1024, re.compile(r'(^[-+]?\d*\.?\d+)([KMGT]i?)?(b|bit|B)$')), |  | ||||||
|     'SI': (1000, re.compile(r'(^[-+]?\d*\.?\d+)([kMGT])?(b|bit|B)$')), |  | ||||||
| } |  | ||||||
|  |  | ||||||
| TRUE_STRINGS = ('1', 't', 'true', 'on', 'y', 'yes') |  | ||||||
| FALSE_STRINGS = ('0', 'f', 'false', 'off', 'n', 'no') |  | ||||||
|  |  | ||||||
| SLUGIFY_STRIP_RE = re.compile(r"[^\w\s-]") |  | ||||||
| SLUGIFY_HYPHENATE_RE = re.compile(r"[-\s]+") |  | ||||||
|  |  | ||||||
|  |  | ||||||
| # NOTE(flaper87): The following globals are used by `mask_password` |  | ||||||
| _SANITIZE_KEYS = ['adminPass', 'admin_pass', 'password', 'admin_password'] |  | ||||||
|  |  | ||||||
| # NOTE(ldbragst): Let's build a list of regex objects using the list of |  | ||||||
| # _SANITIZE_KEYS we already have. This way, we only have to add the new key |  | ||||||
| # to the list of _SANITIZE_KEYS and we can generate regular expressions |  | ||||||
| # for XML and JSON automatically. |  | ||||||
| _SANITIZE_PATTERNS_2 = [] |  | ||||||
| _SANITIZE_PATTERNS_1 = [] |  | ||||||
|  |  | ||||||
| # NOTE(amrith): Some regular expressions have only one parameter, some |  | ||||||
| # have two parameters. Use different lists of patterns here. |  | ||||||
| _FORMAT_PATTERNS_1 = [r'(%(key)s\s*[=]\s*)[^\s^\'^\"]+'] |  | ||||||
| _FORMAT_PATTERNS_2 = [r'(%(key)s\s*[=]\s*[\"\']).*?([\"\'])', |  | ||||||
|                       r'(%(key)s\s+[\"\']).*?([\"\'])', |  | ||||||
|                       r'([-]{2}%(key)s\s+)[^\'^\"^=^\s]+([\s]*)', |  | ||||||
|                       r'(<%(key)s>).*?(</%(key)s>)', |  | ||||||
|                       r'([\"\']%(key)s[\"\']\s*:\s*[\"\']).*?([\"\'])', |  | ||||||
|                       r'([\'"].*?%(key)s[\'"]\s*:\s*u?[\'"]).*?([\'"])', |  | ||||||
|                       r'([\'"].*?%(key)s[\'"]\s*,\s*\'--?[A-z]+\'\s*,\s*u?' |  | ||||||
|                       '[\'"]).*?([\'"])', |  | ||||||
|                       r'(%(key)s\s*--?[A-z]+\s*)\S+(\s*)'] |  | ||||||
|  |  | ||||||
| for key in _SANITIZE_KEYS: |  | ||||||
|     for pattern in _FORMAT_PATTERNS_2: |  | ||||||
|         reg_ex = re.compile(pattern % {'key': key}, re.DOTALL) |  | ||||||
|         _SANITIZE_PATTERNS_2.append(reg_ex) |  | ||||||
|  |  | ||||||
|     for pattern in _FORMAT_PATTERNS_1: |  | ||||||
|         reg_ex = re.compile(pattern % {'key': key}, re.DOTALL) |  | ||||||
|         _SANITIZE_PATTERNS_1.append(reg_ex) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def int_from_bool_as_string(subject): |  | ||||||
|     """Interpret a string as a boolean and return either 1 or 0. |  | ||||||
|  |  | ||||||
|     Any string value in: |  | ||||||
|  |  | ||||||
|         ('True', 'true', 'On', 'on', '1') |  | ||||||
|  |  | ||||||
|     is interpreted as a boolean True. |  | ||||||
|  |  | ||||||
|     Useful for JSON-decoded stuff and config file parsing |  | ||||||
|     """ |  | ||||||
|     return bool_from_string(subject) and 1 or 0 |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def bool_from_string(subject, strict=False, default=False): |  | ||||||
|     """Interpret a string as a boolean. |  | ||||||
|  |  | ||||||
|     A case-insensitive match is performed such that strings matching 't', |  | ||||||
|     'true', 'on', 'y', 'yes', or '1' are considered True and, when |  | ||||||
|     `strict=False`, anything else returns the value specified by 'default'. |  | ||||||
|  |  | ||||||
|     Useful for JSON-decoded stuff and config file parsing. |  | ||||||
|  |  | ||||||
|     If `strict=True`, unrecognized values, including None, will raise a |  | ||||||
|     ValueError which is useful when parsing values passed in from an API call. |  | ||||||
|     Strings yielding False are 'f', 'false', 'off', 'n', 'no', or '0'. |  | ||||||
|     """ |  | ||||||
|     if not isinstance(subject, six.string_types): |  | ||||||
|         subject = six.text_type(subject) |  | ||||||
|  |  | ||||||
|     lowered = subject.strip().lower() |  | ||||||
|  |  | ||||||
|     if lowered in TRUE_STRINGS: |  | ||||||
|         return True |  | ||||||
|     elif lowered in FALSE_STRINGS: |  | ||||||
|         return False |  | ||||||
|     elif strict: |  | ||||||
|         acceptable = ', '.join( |  | ||||||
|             "'%s'" % s for s in sorted(TRUE_STRINGS + FALSE_STRINGS)) |  | ||||||
|         msg = _("Unrecognized value '%(val)s', acceptable values are:" |  | ||||||
|                 " %(acceptable)s") % {'val': subject, |  | ||||||
|                                       'acceptable': acceptable} |  | ||||||
|         raise ValueError(msg) |  | ||||||
|     else: |  | ||||||
|         return default |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def safe_decode(text, incoming=None, errors='strict'): |  | ||||||
|     """Decodes incoming text/bytes string using `incoming` if they're not |  | ||||||
|        already unicode. |  | ||||||
|  |  | ||||||
|     :param incoming: Text's current encoding |  | ||||||
|     :param errors: Errors handling policy. See here for valid |  | ||||||
|         values http://docs.python.org/2/library/codecs.html |  | ||||||
|     :returns: text or a unicode `incoming` encoded |  | ||||||
|                 representation of it. |  | ||||||
|     :raises TypeError: If text is not an instance of str |  | ||||||
|     """ |  | ||||||
|     if not isinstance(text, (six.string_types, six.binary_type)): |  | ||||||
|         raise TypeError("%s can't be decoded" % type(text)) |  | ||||||
|  |  | ||||||
|     if isinstance(text, six.text_type): |  | ||||||
|         return text |  | ||||||
|  |  | ||||||
|     if not incoming: |  | ||||||
|         incoming = (sys.stdin.encoding or |  | ||||||
|                     sys.getdefaultencoding()) |  | ||||||
|  |  | ||||||
|     try: |  | ||||||
|         return text.decode(incoming, errors) |  | ||||||
|     except UnicodeDecodeError: |  | ||||||
|         # Note(flaper87) If we get here, it means that |  | ||||||
|         # sys.stdin.encoding / sys.getdefaultencoding |  | ||||||
|         # didn't return a suitable encoding to decode |  | ||||||
|         # text. This happens mostly when global LANG |  | ||||||
|         # var is not set correctly and there's no |  | ||||||
|         # default encoding. In this case, most likely |  | ||||||
|         # python will use ASCII or ANSI encoders as |  | ||||||
|         # default encodings but they won't be capable |  | ||||||
|         # of decoding non-ASCII characters. |  | ||||||
|         # |  | ||||||
|         # Also, UTF-8 is being used since it's an ASCII |  | ||||||
|         # extension. |  | ||||||
|         return text.decode('utf-8', errors) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def safe_encode(text, incoming=None, |  | ||||||
|                 encoding='utf-8', errors='strict'): |  | ||||||
|     """Encodes incoming text/bytes string using `encoding`. |  | ||||||
|  |  | ||||||
|     If incoming is not specified, text is expected to be encoded with |  | ||||||
|     current python's default encoding. (`sys.getdefaultencoding`) |  | ||||||
|  |  | ||||||
|     :param incoming: Text's current encoding |  | ||||||
|     :param encoding: Expected encoding for text (Default UTF-8) |  | ||||||
|     :param errors: Errors handling policy. See here for valid |  | ||||||
|         values http://docs.python.org/2/library/codecs.html |  | ||||||
|     :returns: text or a bytestring `encoding` encoded |  | ||||||
|                 representation of it. |  | ||||||
|     :raises TypeError: If text is not an instance of str |  | ||||||
|     """ |  | ||||||
|     if not isinstance(text, (six.string_types, six.binary_type)): |  | ||||||
|         raise TypeError("%s can't be encoded" % type(text)) |  | ||||||
|  |  | ||||||
|     if not incoming: |  | ||||||
|         incoming = (sys.stdin.encoding or |  | ||||||
|                     sys.getdefaultencoding()) |  | ||||||
|  |  | ||||||
|     if isinstance(text, six.text_type): |  | ||||||
|         return text.encode(encoding, errors) |  | ||||||
|     elif text and encoding != incoming: |  | ||||||
|         # Decode text before encoding it with `encoding` |  | ||||||
|         text = safe_decode(text, incoming, errors) |  | ||||||
|         return text.encode(encoding, errors) |  | ||||||
|     else: |  | ||||||
|         return text |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def string_to_bytes(text, unit_system='IEC', return_int=False): |  | ||||||
|     """Converts a string into an float representation of bytes. |  | ||||||
|  |  | ||||||
|     The units supported for IEC :: |  | ||||||
|  |  | ||||||
|         Kb(it), Kib(it), Mb(it), Mib(it), Gb(it), Gib(it), Tb(it), Tib(it) |  | ||||||
|         KB, KiB, MB, MiB, GB, GiB, TB, TiB |  | ||||||
|  |  | ||||||
|     The units supported for SI :: |  | ||||||
|  |  | ||||||
|         kb(it), Mb(it), Gb(it), Tb(it) |  | ||||||
|         kB, MB, GB, TB |  | ||||||
|  |  | ||||||
|     Note that the SI unit system does not support capital letter 'K' |  | ||||||
|  |  | ||||||
|     :param text: String input for bytes size conversion. |  | ||||||
|     :param unit_system: Unit system for byte size conversion. |  | ||||||
|     :param return_int: If True, returns integer representation of text |  | ||||||
|                        in bytes. (default: decimal) |  | ||||||
|     :returns: Numerical representation of text in bytes. |  | ||||||
|     :raises ValueError: If text has an invalid value. |  | ||||||
|  |  | ||||||
|     """ |  | ||||||
|     try: |  | ||||||
|         base, reg_ex = UNIT_SYSTEM_INFO[unit_system] |  | ||||||
|     except KeyError: |  | ||||||
|         msg = _('Invalid unit system: "%s"') % unit_system |  | ||||||
|         raise ValueError(msg) |  | ||||||
|     match = reg_ex.match(text) |  | ||||||
|     if match: |  | ||||||
|         magnitude = float(match.group(1)) |  | ||||||
|         unit_prefix = match.group(2) |  | ||||||
|         if match.group(3) in ['b', 'bit']: |  | ||||||
|             magnitude /= 8 |  | ||||||
|     else: |  | ||||||
|         msg = _('Invalid string format: %s') % text |  | ||||||
|         raise ValueError(msg) |  | ||||||
|     if not unit_prefix: |  | ||||||
|         res = magnitude |  | ||||||
|     else: |  | ||||||
|         res = magnitude * pow(base, UNIT_PREFIX_EXPONENT[unit_prefix]) |  | ||||||
|     if return_int: |  | ||||||
|         return int(math.ceil(res)) |  | ||||||
|     return res |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def to_slug(value, incoming=None, errors="strict"): |  | ||||||
|     """Normalize string. |  | ||||||
|  |  | ||||||
|     Convert to lowercase, remove non-word characters, and convert spaces |  | ||||||
|     to hyphens. |  | ||||||
|  |  | ||||||
|     Inspired by Django's `slugify` filter. |  | ||||||
|  |  | ||||||
|     :param value: Text to slugify |  | ||||||
|     :param incoming: Text's current encoding |  | ||||||
|     :param errors: Errors handling policy. See here for valid |  | ||||||
|         values http://docs.python.org/2/library/codecs.html |  | ||||||
|     :returns: slugified unicode representation of `value` |  | ||||||
|     :raises TypeError: If text is not an instance of str |  | ||||||
|     """ |  | ||||||
|     value = safe_decode(value, incoming, errors) |  | ||||||
|     # NOTE(aababilov): no need to use safe_(encode|decode) here: |  | ||||||
|     # encodings are always "ascii", error handling is always "ignore" |  | ||||||
|     # and types are always known (first: unicode; second: str) |  | ||||||
|     value = unicodedata.normalize("NFKD", value).encode( |  | ||||||
|         "ascii", "ignore").decode("ascii") |  | ||||||
|     value = SLUGIFY_STRIP_RE.sub("", value).strip().lower() |  | ||||||
|     return SLUGIFY_HYPHENATE_RE.sub("-", value) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def mask_password(message, secret="***"): |  | ||||||
|     """Replace password with 'secret' in message. |  | ||||||
|  |  | ||||||
|     :param message: The string which includes security information. |  | ||||||
|     :param secret: value with which to replace passwords. |  | ||||||
|     :returns: The unicode value of message with the password fields masked. |  | ||||||
|  |  | ||||||
|     For example: |  | ||||||
|  |  | ||||||
|     >>> mask_password("'adminPass' : 'aaaaa'") |  | ||||||
|     "'adminPass' : '***'" |  | ||||||
|     >>> mask_password("'admin_pass' : 'aaaaa'") |  | ||||||
|     "'admin_pass' : '***'" |  | ||||||
|     >>> mask_password('"password" : "aaaaa"') |  | ||||||
|     '"password" : "***"' |  | ||||||
|     >>> mask_password("'original_password' : 'aaaaa'") |  | ||||||
|     "'original_password' : '***'" |  | ||||||
|     >>> mask_password("u'original_password' :   u'aaaaa'") |  | ||||||
|     "u'original_password' :   u'***'" |  | ||||||
|     """ |  | ||||||
|     message = six.text_type(message) |  | ||||||
|  |  | ||||||
|     # NOTE(ldbragst): Check to see if anything in message contains any key |  | ||||||
|     # specified in _SANITIZE_KEYS, if not then just return the message since |  | ||||||
|     # we don't have to mask any passwords. |  | ||||||
|     if not any(key in message for key in _SANITIZE_KEYS): |  | ||||||
|         return message |  | ||||||
|  |  | ||||||
|     substitute = r'\g<1>' + secret + r'\g<2>' |  | ||||||
|     for pattern in _SANITIZE_PATTERNS_2: |  | ||||||
|         message = re.sub(pattern, substitute, message) |  | ||||||
|  |  | ||||||
|     substitute = r'\g<1>' + secret |  | ||||||
|     for pattern in _SANITIZE_PATTERNS_1: |  | ||||||
|         message = re.sub(pattern, substitute, message) |  | ||||||
|  |  | ||||||
|     return message |  | ||||||
| @@ -1,210 +0,0 @@ | |||||||
| # Copyright 2011 OpenStack Foundation. |  | ||||||
| # All Rights Reserved. |  | ||||||
| # |  | ||||||
| #    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. |  | ||||||
|  |  | ||||||
| """ |  | ||||||
| Time related utilities and helper functions. |  | ||||||
| """ |  | ||||||
|  |  | ||||||
| import calendar |  | ||||||
| import datetime |  | ||||||
| import time |  | ||||||
|  |  | ||||||
| import iso8601 |  | ||||||
| import six |  | ||||||
|  |  | ||||||
|  |  | ||||||
| # ISO 8601 extended time format with microseconds |  | ||||||
| _ISO8601_TIME_FORMAT_SUBSECOND = '%Y-%m-%dT%H:%M:%S.%f' |  | ||||||
| _ISO8601_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S' |  | ||||||
| PERFECT_TIME_FORMAT = _ISO8601_TIME_FORMAT_SUBSECOND |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def isotime(at=None, subsecond=False): |  | ||||||
|     """Stringify time in ISO 8601 format.""" |  | ||||||
|     if not at: |  | ||||||
|         at = utcnow() |  | ||||||
|     st = at.strftime(_ISO8601_TIME_FORMAT |  | ||||||
|                      if not subsecond |  | ||||||
|                      else _ISO8601_TIME_FORMAT_SUBSECOND) |  | ||||||
|     tz = at.tzinfo.tzname(None) if at.tzinfo else 'UTC' |  | ||||||
|     st += ('Z' if tz == 'UTC' else tz) |  | ||||||
|     return st |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def parse_isotime(timestr): |  | ||||||
|     """Parse time from ISO 8601 format.""" |  | ||||||
|     try: |  | ||||||
|         return iso8601.parse_date(timestr) |  | ||||||
|     except iso8601.ParseError as e: |  | ||||||
|         raise ValueError(six.text_type(e)) |  | ||||||
|     except TypeError as e: |  | ||||||
|         raise ValueError(six.text_type(e)) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def strtime(at=None, fmt=PERFECT_TIME_FORMAT): |  | ||||||
|     """Returns formatted utcnow.""" |  | ||||||
|     if not at: |  | ||||||
|         at = utcnow() |  | ||||||
|     return at.strftime(fmt) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def parse_strtime(timestr, fmt=PERFECT_TIME_FORMAT): |  | ||||||
|     """Turn a formatted time back into a datetime.""" |  | ||||||
|     return datetime.datetime.strptime(timestr, fmt) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def normalize_time(timestamp): |  | ||||||
|     """Normalize time in arbitrary timezone to UTC naive object.""" |  | ||||||
|     offset = timestamp.utcoffset() |  | ||||||
|     if offset is None: |  | ||||||
|         return timestamp |  | ||||||
|     return timestamp.replace(tzinfo=None) - offset |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def is_older_than(before, seconds): |  | ||||||
|     """Return True if before is older than seconds.""" |  | ||||||
|     if isinstance(before, six.string_types): |  | ||||||
|         before = parse_strtime(before).replace(tzinfo=None) |  | ||||||
|     else: |  | ||||||
|         before = before.replace(tzinfo=None) |  | ||||||
|  |  | ||||||
|     return utcnow() - before > datetime.timedelta(seconds=seconds) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def is_newer_than(after, seconds): |  | ||||||
|     """Return True if after is newer than seconds.""" |  | ||||||
|     if isinstance(after, six.string_types): |  | ||||||
|         after = parse_strtime(after).replace(tzinfo=None) |  | ||||||
|     else: |  | ||||||
|         after = after.replace(tzinfo=None) |  | ||||||
|  |  | ||||||
|     return after - utcnow() > datetime.timedelta(seconds=seconds) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def utcnow_ts(): |  | ||||||
|     """Timestamp version of our utcnow function.""" |  | ||||||
|     if utcnow.override_time is None: |  | ||||||
|         # NOTE(kgriffs): This is several times faster |  | ||||||
|         # than going through calendar.timegm(...) |  | ||||||
|         return int(time.time()) |  | ||||||
|  |  | ||||||
|     return calendar.timegm(utcnow().timetuple()) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def utcnow(): |  | ||||||
|     """Overridable version of utils.utcnow.""" |  | ||||||
|     if utcnow.override_time: |  | ||||||
|         try: |  | ||||||
|             return utcnow.override_time.pop(0) |  | ||||||
|         except AttributeError: |  | ||||||
|             return utcnow.override_time |  | ||||||
|     return datetime.datetime.utcnow() |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def iso8601_from_timestamp(timestamp): |  | ||||||
|     """Returns an iso8601 formatted date from timestamp.""" |  | ||||||
|     return isotime(datetime.datetime.utcfromtimestamp(timestamp)) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| utcnow.override_time = None |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def set_time_override(override_time=None): |  | ||||||
|     """Overrides utils.utcnow. |  | ||||||
|  |  | ||||||
|     Make it return a constant time or a list thereof, one at a time. |  | ||||||
|  |  | ||||||
|     :param override_time: datetime instance or list thereof. If not |  | ||||||
|                           given, defaults to the current UTC time. |  | ||||||
|     """ |  | ||||||
|     utcnow.override_time = override_time or datetime.datetime.utcnow() |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def advance_time_delta(timedelta): |  | ||||||
|     """Advance overridden time using a datetime.timedelta.""" |  | ||||||
|     assert utcnow.override_time is not None |  | ||||||
|     try: |  | ||||||
|         for dt in utcnow.override_time: |  | ||||||
|             dt += timedelta |  | ||||||
|     except TypeError: |  | ||||||
|         utcnow.override_time += timedelta |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def advance_time_seconds(seconds): |  | ||||||
|     """Advance overridden time by seconds.""" |  | ||||||
|     advance_time_delta(datetime.timedelta(0, seconds)) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def clear_time_override(): |  | ||||||
|     """Remove the overridden time.""" |  | ||||||
|     utcnow.override_time = None |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def marshall_now(now=None): |  | ||||||
|     """Make an rpc-safe datetime with microseconds. |  | ||||||
|  |  | ||||||
|     Note: tzinfo is stripped, but not required for relative times. |  | ||||||
|     """ |  | ||||||
|     if not now: |  | ||||||
|         now = utcnow() |  | ||||||
|     return dict(day=now.day, month=now.month, year=now.year, hour=now.hour, |  | ||||||
|                 minute=now.minute, second=now.second, |  | ||||||
|                 microsecond=now.microsecond) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def unmarshall_time(tyme): |  | ||||||
|     """Unmarshall a datetime dict.""" |  | ||||||
|     return datetime.datetime(day=tyme['day'], |  | ||||||
|                              month=tyme['month'], |  | ||||||
|                              year=tyme['year'], |  | ||||||
|                              hour=tyme['hour'], |  | ||||||
|                              minute=tyme['minute'], |  | ||||||
|                              second=tyme['second'], |  | ||||||
|                              microsecond=tyme['microsecond']) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def delta_seconds(before, after): |  | ||||||
|     """Return the difference between two timing objects. |  | ||||||
|  |  | ||||||
|     Compute the difference in seconds between two date, time, or |  | ||||||
|     datetime objects (as a float, to microsecond resolution). |  | ||||||
|     """ |  | ||||||
|     delta = after - before |  | ||||||
|     return total_seconds(delta) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def total_seconds(delta): |  | ||||||
|     """Return the total seconds of datetime.timedelta object. |  | ||||||
|  |  | ||||||
|     Compute total seconds of datetime.timedelta, datetime.timedelta |  | ||||||
|     doesn't have method total_seconds in Python2.6, calculate it manually. |  | ||||||
|     """ |  | ||||||
|     try: |  | ||||||
|         return delta.total_seconds() |  | ||||||
|     except AttributeError: |  | ||||||
|         return ((delta.days * 24 * 3600) + delta.seconds + |  | ||||||
|                 float(delta.microseconds) / (10 ** 6)) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def is_soon(dt, window): |  | ||||||
|     """Determines if time is going to happen in the next window seconds. |  | ||||||
|  |  | ||||||
|     :param dt: the time |  | ||||||
|     :param window: minimum seconds to remain to consider the time not soon |  | ||||||
|  |  | ||||||
|     :returns: True if expiration is within the given duration |  | ||||||
|     """ |  | ||||||
|     soon = (utcnow() + datetime.timedelta(seconds=window)) |  | ||||||
|     return normalize_time(dt) <= soon |  | ||||||
							
								
								
									
										37
									
								
								keystoneclient/openstack/common/uuidutils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								keystoneclient/openstack/common/uuidutils.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | |||||||
|  | # Copyright (c) 2012 Intel Corporation. | ||||||
|  | # All Rights Reserved. | ||||||
|  | # | ||||||
|  | #    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. | ||||||
|  |  | ||||||
|  | """ | ||||||
|  | UUID related utilities and helper functions. | ||||||
|  | """ | ||||||
|  |  | ||||||
|  | import uuid | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def generate_uuid(): | ||||||
|  |     return str(uuid.uuid4()) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def is_uuid_like(val): | ||||||
|  |     """Returns validation of a value as a UUID. | ||||||
|  |  | ||||||
|  |     For our purposes, a UUID is a canonical form string: | ||||||
|  |     aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa | ||||||
|  |  | ||||||
|  |     """ | ||||||
|  |     try: | ||||||
|  |         return str(uuid.UUID(val)) == val | ||||||
|  |     except (TypeError, ValueError, AttributeError): | ||||||
|  |         return False | ||||||
| @@ -1007,7 +1007,7 @@ class CommonAuthTokenMiddlewareTest(object): | |||||||
|         token = self.token_dict['signed_token_scoped'] |         token = self.token_dict['signed_token_scoped'] | ||||||
|         req.headers['X-Auth-Token'] = token |         req.headers['X-Auth-Token'] = token | ||||||
|         req.environ.update(extra_environ) |         req.environ.update(extra_environ) | ||||||
|         timeutils_utcnow = 'keystoneclient.openstack.common.timeutils.utcnow' |         timeutils_utcnow = 'oslo.utils.timeutils.utcnow' | ||||||
|         now = datetime.datetime.utcnow() |         now = datetime.datetime.utcnow() | ||||||
|         with mock.patch(timeutils_utcnow) as mock_utcnow: |         with mock.patch(timeutils_utcnow) as mock_utcnow: | ||||||
|             mock_utcnow.return_value = now |             mock_utcnow.return_value = now | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Jenkins
					Jenkins