From b4ef81d6a018a3e6781bbfd5c9a2c73727645cc5 Mon Sep 17 00:00:00 2001 From: Gary Kotton Date: Sun, 2 Mar 2014 00:04:24 -0800 Subject: [PATCH] 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 --- HACKING.rst | 1 + nova/api/ec2/__init__.py | 2 +- nova/api/openstack/__init__.py | 2 +- nova/compute/manager.py | 3 ++- nova/console/websocketproxy.py | 10 ++++++---- nova/exception.py | 2 +- nova/hacking/checks.py | 18 ++++++++++++++++++ nova/tests/test_hacking.py | 31 +++++++++++++++++++++++++++++++ nova/virt/libvirt/vif.py | 4 ++-- 9 files changed, 63 insertions(+), 10 deletions(-) diff --git a/HACKING.rst b/HACKING.rst index cbdee68822f5..1fa7dab6de4f 100644 --- a/HACKING.rst +++ b/HACKING.rst @@ -33,6 +33,7 @@ Nova Specific Commandments - [N319] Validate that debug level logs are not translated. - [N320] Setting CONF.* attributes directly in tests is forbidden. Use self.flags(option=value) instead. +- [N321] Validate that LOG messages, except debug ones, have translations Creating Unit Tests ------------------- diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index 1c8de65e3044..1dad7bf23090 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -123,7 +123,7 @@ class RequestLogging(wsgi.Middleware): request.user_agent, request.content_type, response.content_type, - context=ctxt) + context=ctxt) # noqa class Lockout(wsgi.Middleware): diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py index a047b06a630b..cac785241d03 100644 --- a/nova/api/openstack/__init__.py +++ b/nova/api/openstack/__init__.py @@ -292,7 +292,7 @@ class APIRouterV3(base_wsgi.Router): return False 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 self.init_only = init_only diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 58f620f5fdfd..e004c1ba5ce5 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -2024,7 +2024,8 @@ class ComputeManager(manager.Manager): except Exception: # Because this allocation is async any failures are likely to occur # 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.') raise exception.BuildAbortException(instance_uuid=instance.uuid, reason=msg) diff --git a/nova/console/websocketproxy.py b/nova/console/websocketproxy.py index e5bfb9ef1630..c51078dcff77 100644 --- a/nova/console/websocketproxy.py +++ b/nova/console/websocketproxy.py @@ -52,7 +52,7 @@ class NovaWebSocketProxy(websockify.WebSocketProxy): connect_info = rpcapi.check_token(ctxt, token=token) if not connect_info: - LOG.audit("Invalid Token: %s", token) + LOG.audit(_("Invalid Token: %s"), token) raise Exception(_("Invalid Token")) host = connect_info['host'] @@ -60,7 +60,8 @@ class NovaWebSocketProxy(websockify.WebSocketProxy): # Connect to the target 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) # Handshake as necessary @@ -71,7 +72,7 @@ class NovaWebSocketProxy(websockify.WebSocketProxy): data = tsock.recv(4096, socket.MSG_PEEK) if data.find("\r\n\r\n") != -1: 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")) tsock.recv(len(data)) break @@ -87,5 +88,6 @@ class NovaWebSocketProxy(websockify.WebSocketProxy): tsock.shutdown(socket.SHUT_RDWR) tsock.close() 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 diff --git a/nova/exception.py b/nova/exception.py index a0c5cae87968..efa5545afb69 100644 --- a/nova/exception.py +++ b/nova/exception.py @@ -123,7 +123,7 @@ class NovaException(Exception): # log the issue and the kwargs LOG.exception(_('Exception in string format operation')) 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: raise exc_info[0], exc_info[1], exc_info[2] diff --git a/nova/hacking/checks.py b/nova/hacking/checks.py index c35b18a76b4e..f062155570a6 100644 --- a/nova/hacking/checks.py +++ b/nova/hacking/checks.py @@ -15,6 +15,8 @@ import re +import pep8 + """ 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( r"(.)*assertEqual\(None, (\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): @@ -260,6 +264,19 @@ def no_setting_conf_directly_in_tests(logical_line, filename): "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): register(import_no_db_in_virt) register(no_db_session_in_public_api) @@ -274,3 +291,4 @@ def factory(register): register(assert_equal_none) register(no_translate_debug_logs) register(no_setting_conf_directly_in_tests) + register(validate_log_translations) diff --git a/nova/tests/test_hacking.py b/nova/tests/test_hacking.py index d4b689d8ab6a..843fb164237a 100644 --- a/nova/tests/test_hacking.py +++ b/nova/tests/test_hacking.py @@ -144,3 +144,34 @@ class HackingTestCase(test.NoDBTestCase): # Shouldn't fail since not in nova/tests/ self.assertEqual(len(list(checks.no_setting_conf_directly_in_tests( "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')))) diff --git a/nova/virt/libvirt/vif.py b/nova/virt/libvirt/vif.py index 263866c38d82..6965d79ac042 100644 --- a/nova/virt/libvirt/vif.py +++ b/nova/virt/libvirt/vif.py @@ -817,8 +817,8 @@ class LibvirtGenericVIFDriver(LibvirtBaseVIFDriver): class _LibvirtDeprecatedDriver(LibvirtGenericVIFDriver): def __init__(self, *args, **kwargs): - LOG.warn('VIF driver \"%s\" is marked as deprecated and will be ' - 'removed in the Juno release.', + LOG.warn(_('VIF driver \"%s\" is marked as deprecated and will be ' + 'removed in the Juno release.'), self.__class__.__name__) super(_LibvirtDeprecatedDriver, self).__init__(*args, **kwargs)