Add unicode support for resource name

When using template which has resource name in unicode to create
stack will failed with encode/decode error.
This patch override __str__ & __unicode__ function in related
classes to make them compatible with unicode.

blueprint: template-unicode-support
Change-Id: Ie677d180d2131abd052101996900414e2b2ef4ad
This commit is contained in:
Ethan Lynn 2014-10-13 17:58:57 +08:00
parent 0becf6c08f
commit 04f0271931
4 changed files with 85 additions and 31 deletions

View File

@ -14,6 +14,7 @@
import collections import collections
import itertools import itertools
from oslo.utils import encodeutils
import six import six
from six.moves import xrange from six.moves import xrange
@ -84,7 +85,13 @@ class Node(object):
def __str__(self): def __str__(self):
'''Return a human-readable string representation of the node.''' '''Return a human-readable string representation of the node.'''
return '{%s}' % ', '.join(str(n) for n in self) text = '{%s}' % ', '.join(str(n) for n in self)
return encodeutils.safe_encode(text)
def __unicode__(self):
'''Return a human-readable string representation of the node.'''
text = '{%s}' % ', '.join(unicode(n) for n in self)
return encodeutils.safe_decode(text)
def __repr__(self): def __repr__(self):
'''Return a string representation of the node.''' '''Return a string representation of the node.'''
@ -137,7 +144,15 @@ class Graph(collections.defaultdict):
def __str__(self): def __str__(self):
'''Convert the graph to a human-readable string.''' '''Convert the graph to a human-readable string.'''
pairs = ('%s: %s' % (str(k), str(v)) for k, v in six.iteritems(self)) pairs = ('%s: %s' % (str(k), str(v)) for k, v in six.iteritems(self))
return '{%s}' % ', '.join(pairs) text = '{%s}' % ', '.join(pairs)
return encodeutils.safe_encode(text)
def __unicode__(self):
'''Convert the graph to a human-readable string.'''
pairs = ('%s: %s' % (unicode(k), unicode(v))
for k, v in six.iteritems(self))
text = '{%s}' % ', '.join(pairs)
return encodeutils.safe_decode(text)
@staticmethod @staticmethod
def toposort(graph): def toposort(graph):
@ -155,7 +170,7 @@ class Graph(collections.defaultdict):
else: else:
# There are nodes remaining, but none without # There are nodes remaining, but none without
# dependencies: a cycle # dependencies: a cycle
raise CircularDependencyException(cycle=str(graph)) raise CircularDependencyException(cycle=six.text_type(graph))
class Dependencies(object): class Dependencies(object):
@ -227,10 +242,17 @@ class Dependencies(object):
''' '''
return str(self._graph) return str(self._graph)
def __unicode__(self):
'''
Return a human-readable string representation of the dependency graph
'''
return unicode(self._graph)
def __repr__(self): def __repr__(self):
'''Return a string representation of the object.''' '''Return a string representation of the object.'''
edge_reprs = (repr(e) for e in self._graph.edges()) edge_reprs = (repr(e) for e in self._graph.edges())
return 'Dependencies([%s])' % ', '.join(edge_reprs) text = 'Dependencies([%s])' % ', '.join(edge_reprs)
return encodeutils.safe_encode(text)
def graph(self, reverse=False): def graph(self, reverse=False):
'''Return a copy of the underlying dependency graph.''' '''Return a copy of the underlying dependency graph.'''

View File

@ -15,6 +15,7 @@ import base64
import contextlib import contextlib
from datetime import datetime from datetime import datetime
from oslo.config import cfg from oslo.config import cfg
from oslo.utils import encodeutils
from oslo.utils import excutils from oslo.utils import excutils
import six import six
import warnings import warnings
@ -365,11 +366,27 @@ class Resource(object):
def __str__(self): def __str__(self):
if self.stack.id: if self.stack.id:
if self.resource_id: if self.resource_id:
return '%s "%s" [%s] %s' % (self.__class__.__name__, self.name, text = '%s "%s" [%s] %s' % (self.__class__.__name__, self.name,
self.resource_id, str(self.stack)) self.resource_id, str(self.stack))
return '%s "%s" %s' % (self.__class__.__name__, self.name, else:
str(self.stack)) text = '%s "%s" %s' % (self.__class__.__name__, self.name,
return '%s "%s"' % (self.__class__.__name__, self.name) str(self.stack))
else:
text = '%s "%s"' % (self.__class__.__name__, self.name)
return encodeutils.safe_encode(text)
def __unicode__(self):
if self.stack.id:
if self.resource_id:
text = '%s "%s" [%s] %s' % (self.__class__.__name__, self.name,
self.resource_id,
unicode(self.stack))
else:
text = '%s "%s" %s' % (self.__class__.__name__, self.name,
unicode(self.stack))
else:
text = '%s "%s"' % (self.__class__.__name__, self.name)
return encodeutils.safe_decode(text)
def add_dependencies(self, deps): def add_dependencies(self, deps):
for dep in self.t.dependencies(self.stack): for dep in self.t.dependencies(self.stack):
@ -441,7 +458,7 @@ class Resource(object):
LOG.debug('%s', six.text_type(ex)) LOG.debug('%s', six.text_type(ex))
except Exception as ex: except Exception as ex:
LOG.info('%(action)s: %(info)s', {"action": action, LOG.info('%(action)s: %(info)s', {"action": action,
"info": str(self)}, "info": six.text_type(self)},
exc_info=True) exc_info=True)
failure = exception.ResourceFailure(ex, self, action) failure = exception.ResourceFailure(ex, self, action)
self.state_set(action, self.FAILED, six.text_type(failure)) self.state_set(action, self.FAILED, six.text_type(failure))
@ -528,10 +545,10 @@ class Resource(object):
action = self.CREATE action = self.CREATE
if (self.action, self.status) != (self.INIT, self.COMPLETE): if (self.action, self.status) != (self.INIT, self.COMPLETE):
exc = exception.Error(_('State %s invalid for create') exc = exception.Error(_('State %s invalid for create')
% str(self.state)) % six.text_type(self.state))
raise exception.ResourceFailure(exc, self, action) raise exception.ResourceFailure(exc, self, action)
LOG.info(_LI('creating %s'), str(self)) LOG.info(_LI('creating %s'), six.text_type(self))
# Re-resolve the template, since if the resource Ref's # Re-resolve the template, since if the resource Ref's
# the StackId pseudo parameter, it will change after # the StackId pseudo parameter, it will change after
@ -723,7 +740,7 @@ class Resource(object):
# Don't try to suspend the resource unless it's in a stable state # Don't try to suspend the resource unless it's in a stable state
if (self.action == self.DELETE or self.status != self.COMPLETE): if (self.action == self.DELETE or self.status != self.COMPLETE):
exc = exception.Error(_('State %s invalid for suspend') exc = exception.Error(_('State %s invalid for suspend')
% str(self.state)) % six.text_type(self.state))
raise exception.ResourceFailure(exc, self, action) raise exception.ResourceFailure(exc, self, action)
LOG.info(_LI('suspending %s'), six.text_type(self)) LOG.info(_LI('suspending %s'), six.text_type(self))
@ -739,7 +756,7 @@ class Resource(object):
# Can't resume a resource unless it's SUSPEND_COMPLETE # Can't resume a resource unless it's SUSPEND_COMPLETE
if self.state != (self.SUSPEND, self.COMPLETE): if self.state != (self.SUSPEND, self.COMPLETE):
exc = exception.Error(_('State %s invalid for resume') exc = exception.Error(_('State %s invalid for resume')
% str(self.state)) % six.text_type(self.state))
raise exception.ResourceFailure(exc, self, action) raise exception.ResourceFailure(exc, self, action)
LOG.info(_LI('resuming %s'), six.text_type(self)) LOG.info(_LI('resuming %s'), six.text_type(self))
@ -1039,8 +1056,8 @@ class Resource(object):
reason_string = get_string_details() reason_string = get_string_details()
self._add_event('signal', self.status, reason_string) self._add_event('signal', self.status, reason_string)
except Exception as ex: except Exception as ex:
LOG.exception(_('signal %(name)s : %(msg)s') % {'name': str(self), LOG.exception(_('signal %(name)s : %(msg)s')
'msg': ex}) % {'name': six.text_type(self), 'msg': ex})
failure = exception.ResourceFailure(ex, self) failure = exception.ResourceFailure(ex, self)
raise failure raise failure

View File

@ -18,6 +18,7 @@ from time import time as wallclock
import types import types
import eventlet import eventlet
from oslo.utils import encodeutils
from oslo.utils import excutils from oslo.utils import excutils
import six import six
@ -43,7 +44,7 @@ def task_description(task):
return '%s from %s' % (name, task.__self__) return '%s from %s' % (name, task.__self__)
elif isinstance(task, types.FunctionType): elif isinstance(task, types.FunctionType):
if name is not None: if name is not None:
return str(name) return six.text_type(name)
return repr(task) return repr(task)
@ -62,7 +63,7 @@ class Timeout(BaseException):
""" """
Initialise with the TaskRunner and a timeout period in seconds. Initialise with the TaskRunner and a timeout period in seconds.
""" """
message = _('%s Timed out') % str(task_runner) message = _('%s Timed out') % six.text_type(task_runner)
super(Timeout, self).__init__(message) super(Timeout, self).__init__(message)
# Note that we don't attempt to handle leap seconds or large clock # Note that we don't attempt to handle leap seconds or large clock
@ -148,12 +149,18 @@ class TaskRunner(object):
def __str__(self): def __str__(self):
"""Return a human-readable string representation of the task.""" """Return a human-readable string representation of the task."""
return 'Task %s' % self.name text = 'Task %s' % self.name
return encodeutils.safe_encode(text)
def __unicode__(self):
"""Return a human-readable string representation of the task."""
text = 'Task %s' % self.name
return encodeutils.safe_decode(text)
def _sleep(self, wait_time): def _sleep(self, wait_time):
"""Sleep for the specified number of seconds.""" """Sleep for the specified number of seconds."""
if ENABLE_SLEEP and wait_time is not None: if ENABLE_SLEEP and wait_time is not None:
LOG.debug('%s sleeping' % str(self)) LOG.debug('%s sleeping' % six.text_type(self))
eventlet.sleep(wait_time) eventlet.sleep(wait_time)
def __call__(self, wait_time=1, timeout=None): def __call__(self, wait_time=1, timeout=None):
@ -180,7 +187,7 @@ class TaskRunner(object):
assert self._runner is None, "Task already started" assert self._runner is None, "Task already started"
assert not self._done, "Task already cancelled" assert not self._done, "Task already cancelled"
LOG.debug('%s starting' % str(self)) LOG.debug('%s starting' % six.text_type(self))
if timeout is not None: if timeout is not None:
self._timeout = Timeout(self, timeout) self._timeout = Timeout(self, timeout)
@ -192,7 +199,7 @@ class TaskRunner(object):
else: else:
self._runner = False self._runner = False
self._done = True self._done = True
LOG.debug('%s done (not resumable)' % str(self)) LOG.debug('%s done (not resumable)' % six.text_type(self))
def step(self): def step(self):
""" """
@ -203,18 +210,18 @@ class TaskRunner(object):
assert self._runner is not None, "Task not started" assert self._runner is not None, "Task not started"
if self._timeout is not None and self._timeout.expired(): if self._timeout is not None and self._timeout.expired():
LOG.info(_LI('%s timed out'), str(self)) LOG.info(_LI('%s timed out'), six.text_type(self))
self._done = True self._done = True
self._timeout.trigger(self._runner) self._timeout.trigger(self._runner)
else: else:
LOG.debug('%s running' % str(self)) LOG.debug('%s running' % six.text_type(self))
try: try:
next(self._runner) next(self._runner)
except StopIteration: except StopIteration:
self._done = True self._done = True
LOG.debug('%s complete' % str(self)) LOG.debug('%s complete' % six.text_type(self))
return self._done return self._done
@ -234,7 +241,7 @@ class TaskRunner(object):
return return
if not self.started() or grace_period is None: if not self.started() or grace_period is None:
LOG.debug('%s cancelled' % str(self)) LOG.debug('%s cancelled' % six.text_type(self))
self._done = True self._done = True
if self.started(): if self.started():
self._runner.close() self._runner.close()
@ -351,12 +358,13 @@ class DependencyTaskGroup(object):
if name is None: if name is None:
name = '(%s) %s' % (getattr(task, '__name__', name = '(%s) %s' % (getattr(task, '__name__',
task_description(task)), task_description(task)),
str(dependencies)) six.text_type(dependencies))
self.name = name self.name = name
def __repr__(self): def __repr__(self):
"""Return a string representation of the task.""" """Return a string representation of the task."""
return '%s(%s)' % (type(self).__name__, self.name) text = '%s(%s)' % (type(self).__name__, self.name)
return encodeutils.safe_encode(text)
def __call__(self): def __call__(self):
"""Return a co-routine which runs the task group.""" """Return a co-routine which runs the task group."""
@ -497,7 +505,8 @@ class PollingTaskGroup(object):
def __repr__(self): def __repr__(self):
"""Return a string representation of the task group.""" """Return a string representation of the task group."""
return '%s(%s)' % (type(self).__name__, self.name) text = '%s(%s)' % (type(self).__name__, self.name)
return encodeutils.safe_encode(text)
def __call__(self): def __call__(self):
"""Return a co-routine which runs the task group.""" """Return a co-routine which runs the task group."""

View File

@ -391,7 +391,13 @@ class Stack(collections.Mapping):
def __str__(self): def __str__(self):
'''Return a human-readable string representation of the stack.''' '''Return a human-readable string representation of the stack.'''
return 'Stack "%s" [%s]' % (self.name, self.id) text = 'Stack "%s" [%s]' % (self.name, self.id)
return encodeutils.safe_encode(text)
def __unicode__(self):
'''Return a human-readable string representation of the stack.'''
text = 'Stack "%s" [%s]' % (self.name, self.id)
return encodeutils.safe_encode(text)
def resource_by_refid(self, refid): def resource_by_refid(self, refid):
''' '''
@ -991,7 +997,7 @@ class Stack(collections.Mapping):
''' '''
# No need to suspend if the stack has been suspended # No need to suspend if the stack has been suspended
if self.state == (self.SUSPEND, self.COMPLETE): if self.state == (self.SUSPEND, self.COMPLETE):
LOG.info(_LI('%s is already suspended'), str(self)) LOG.info(_LI('%s is already suspended'), six.text_type(self))
return return
sus_task = scheduler.TaskRunner(self.stack_task, sus_task = scheduler.TaskRunner(self.stack_task,
@ -1011,7 +1017,7 @@ class Stack(collections.Mapping):
''' '''
# No need to resume if the stack has been resumed # No need to resume if the stack has been resumed
if self.state == (self.RESUME, self.COMPLETE): if self.state == (self.RESUME, self.COMPLETE):
LOG.info(_LI('%s is already resumed'), str(self)) LOG.info(_LI('%s is already resumed'), six.text_type(self))
return return
sus_task = scheduler.TaskRunner(self.stack_task, sus_task = scheduler.TaskRunner(self.stack_task,