Add missing translation support

Update a number of files to add missing translation support.

The patch adds a new hacking check - N321. This ensures that
all log messages, except debug ones, have translations.

A '# noqa' indicates that the validation will not be done on
the specific log message. This should be used in cases where
the translations do not need to be done, for example, the log
message is logging raw data.

Closes-bug: #1290261
Change-Id: Ib2ca0bfaaf432e15448c96619682c2cfd073fbbc
This commit is contained in:
Gary Kotton 2014-03-02 00:04:24 -08:00
parent 5d7ac6a928
commit b4ef81d6a0
9 changed files with 63 additions and 10 deletions

View File

@ -33,6 +33,7 @@ Nova Specific Commandments
- [N319] Validate that debug level logs are not translated. - [N319] Validate that debug level logs are not translated.
- [N320] Setting CONF.* attributes directly in tests is forbidden. Use - [N320] Setting CONF.* attributes directly in tests is forbidden. Use
self.flags(option=value) instead. self.flags(option=value) instead.
- [N321] Validate that LOG messages, except debug ones, have translations
Creating Unit Tests Creating Unit Tests
------------------- -------------------

View File

@ -123,7 +123,7 @@ class RequestLogging(wsgi.Middleware):
request.user_agent, request.user_agent,
request.content_type, request.content_type,
response.content_type, response.content_type,
context=ctxt) context=ctxt) # noqa
class Lockout(wsgi.Middleware): class Lockout(wsgi.Middleware):

View File

@ -292,7 +292,7 @@ class APIRouterV3(base_wsgi.Router):
return False return False
if not CONF.osapi_v3.enabled: if not CONF.osapi_v3.enabled:
LOG.info("V3 API has been disabled by configuration") LOG.info(_("V3 API has been disabled by configuration"))
return return
self.init_only = init_only self.init_only = init_only

View File

@ -2024,7 +2024,8 @@ class ComputeManager(manager.Manager):
except Exception: except Exception:
# Because this allocation is async any failures are likely to occur # Because this allocation is async any failures are likely to occur
# when the driver accesses network_info during spawn(). # when the driver accesses network_info during spawn().
LOG.exception('Failed to allocate network(s)', instance=instance) LOG.exception(_('Failed to allocate network(s)'),
instance=instance)
msg = _('Failed to allocate the network(s), not rescheduling.') msg = _('Failed to allocate the network(s), not rescheduling.')
raise exception.BuildAbortException(instance_uuid=instance.uuid, raise exception.BuildAbortException(instance_uuid=instance.uuid,
reason=msg) reason=msg)

View File

@ -52,7 +52,7 @@ class NovaWebSocketProxy(websockify.WebSocketProxy):
connect_info = rpcapi.check_token(ctxt, token=token) connect_info = rpcapi.check_token(ctxt, token=token)
if not connect_info: if not connect_info:
LOG.audit("Invalid Token: %s", token) LOG.audit(_("Invalid Token: %s"), token)
raise Exception(_("Invalid Token")) raise Exception(_("Invalid Token"))
host = connect_info['host'] host = connect_info['host']
@ -60,7 +60,8 @@ class NovaWebSocketProxy(websockify.WebSocketProxy):
# Connect to the target # Connect to the target
self.msg("connecting to: %s:%s" % (host, port)) self.msg("connecting to: %s:%s" % (host, port))
LOG.audit("connecting to: %s:%s" % (host, port)) LOG.audit(_("connecting to: %(host)s:%(port)s"),
{'host': host, 'port': port})
tsock = self.socket(host, port, connect=True) tsock = self.socket(host, port, connect=True)
# Handshake as necessary # Handshake as necessary
@ -71,7 +72,7 @@ class NovaWebSocketProxy(websockify.WebSocketProxy):
data = tsock.recv(4096, socket.MSG_PEEK) data = tsock.recv(4096, socket.MSG_PEEK)
if data.find("\r\n\r\n") != -1: if data.find("\r\n\r\n") != -1:
if not data.split("\r\n")[0].find("200"): if not data.split("\r\n")[0].find("200"):
LOG.audit("Invalid Connection Info %s", token) LOG.audit(_("Invalid Connection Info %s"), token)
raise Exception(_("Invalid Connection Info")) raise Exception(_("Invalid Connection Info"))
tsock.recv(len(data)) tsock.recv(len(data))
break break
@ -87,5 +88,6 @@ class NovaWebSocketProxy(websockify.WebSocketProxy):
tsock.shutdown(socket.SHUT_RDWR) tsock.shutdown(socket.SHUT_RDWR)
tsock.close() tsock.close()
self.vmsg("%s:%s: Target closed" % (host, port)) self.vmsg("%s:%s: Target closed" % (host, port))
LOG.audit("%s:%s: Target closed" % (host, port)) LOG.audit(_("%(host)s:%(port)s: Target closed"),
{'host': host, 'port': port})
raise raise

View File

@ -123,7 +123,7 @@ class NovaException(Exception):
# log the issue and the kwargs # log the issue and the kwargs
LOG.exception(_('Exception in string format operation')) LOG.exception(_('Exception in string format operation'))
for name, value in kwargs.iteritems(): for name, value in kwargs.iteritems():
LOG.error("%s: %s" % (name, value)) LOG.error("%s: %s" % (name, value)) # noqa
if CONF.fatal_exception_format_errors: if CONF.fatal_exception_format_errors:
raise exc_info[0], exc_info[1], exc_info[2] raise exc_info[0], exc_info[1], exc_info[2]

View File

@ -15,6 +15,8 @@
import re import re
import pep8
""" """
Guidelines for writing new hacking checks Guidelines for writing new hacking checks
@ -50,6 +52,8 @@ asse_equal_end_with_none_re = re.compile(
asse_equal_start_with_none_re = re.compile( asse_equal_start_with_none_re = re.compile(
r"(.)*assertEqual\(None, (\w|\.|\'|\"|\[|\])+\)") r"(.)*assertEqual\(None, (\w|\.|\'|\"|\[|\])+\)")
conf_attribute_set_re = re.compile(r"CONF\.[a-z0-9_.]+\s*=\s*\w") conf_attribute_set_re = re.compile(r"CONF\.[a-z0-9_.]+\s*=\s*\w")
log_translation = re.compile(
r"(.)*LOG\.(audit|error|info|warn|warning|critical|exception)\(\s*('|\")")
def import_no_db_in_virt(logical_line, filename): def import_no_db_in_virt(logical_line, filename):
@ -260,6 +264,19 @@ def no_setting_conf_directly_in_tests(logical_line, filename):
"forbidden. Use self.flags(option=value) instead") "forbidden. Use self.flags(option=value) instead")
def validate_log_translations(logical_line, physical_line, filename):
# Translations are not required in the test directory
# and the Xen utilities
if ("nova/tests" in filename or
"plugins/xenserver/xenapi/etc/xapi.d" in filename):
return
if pep8.noqa(physical_line):
return
msg = "N321: Log messages require translations!"
if log_translation.match(logical_line):
yield (0, msg)
def factory(register): def factory(register):
register(import_no_db_in_virt) register(import_no_db_in_virt)
register(no_db_session_in_public_api) register(no_db_session_in_public_api)
@ -274,3 +291,4 @@ def factory(register):
register(assert_equal_none) register(assert_equal_none)
register(no_translate_debug_logs) register(no_translate_debug_logs)
register(no_setting_conf_directly_in_tests) register(no_setting_conf_directly_in_tests)
register(validate_log_translations)

View File

@ -144,3 +144,34 @@ class HackingTestCase(test.NoDBTestCase):
# Shouldn't fail since not in nova/tests/ # Shouldn't fail since not in nova/tests/
self.assertEqual(len(list(checks.no_setting_conf_directly_in_tests( self.assertEqual(len(list(checks.no_setting_conf_directly_in_tests(
"CONF.option = 1", "nova/compute/foo.py"))), 0) "CONF.option = 1", "nova/compute/foo.py"))), 0)
def test_log_translations(self):
logs = ['audit', 'error', 'info', 'warn', 'warning', 'critical',
'exception']
levels = ['_LI', '_LW', '_LE', '_LC']
debug = "LOG.debug('OK')"
self.assertEqual(0,
len(list(
checks.validate_log_translations(debug, debug, 'f'))))
for log in logs:
bad = 'LOG.%s("Bad")' % log
self.assertEqual(1,
len(list(
checks.validate_log_translations(bad, bad, 'f'))))
ok = "LOG.%s(_('OK'))" % log
self.assertEqual(0,
len(list(
checks.validate_log_translations(ok, ok, 'f'))))
ok = "LOG.%s('OK') # noqa" % log
self.assertEqual(0,
len(list(
checks.validate_log_translations(ok, ok, 'f'))))
ok = "LOG.%s(variable)" % log
self.assertEqual(0,
len(list(
checks.validate_log_translations(ok, ok, 'f'))))
for level in levels:
ok = "LOG.%s(%s('OK'))" % (log, level)
self.assertEqual(0,
len(list(
checks.validate_log_translations(ok, ok, 'f'))))

View File

@ -817,8 +817,8 @@ class LibvirtGenericVIFDriver(LibvirtBaseVIFDriver):
class _LibvirtDeprecatedDriver(LibvirtGenericVIFDriver): class _LibvirtDeprecatedDriver(LibvirtGenericVIFDriver):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
LOG.warn('VIF driver \"%s\" is marked as deprecated and will be ' LOG.warn(_('VIF driver \"%s\" is marked as deprecated and will be '
'removed in the Juno release.', 'removed in the Juno release.'),
self.__class__.__name__) self.__class__.__name__)
super(_LibvirtDeprecatedDriver, self).__init__(*args, **kwargs) super(_LibvirtDeprecatedDriver, self).__init__(*args, **kwargs)