Add i18n translation for Log messages

Added i18n translation to all log messages. Also added
hacking check for this to ensure in future all log
messages are translated.

Change-Id: I77fbb18ffe47b12232125cc1fa7aae97ea9b1214
Closes-Bug: #1603759
This commit is contained in:
yatin karel 2016-08-15 11:33:11 +05:30
parent 4b66daafd7
commit fb0057c105
8 changed files with 91 additions and 29 deletions

View File

@ -19,6 +19,7 @@ from k8sclient.client.apis import apiv_api
from oslo_log import log as logging
from magnum.conductor.handlers.common.cert_manager import create_client_files
from magnum.i18n import _LE
LOG = logging.getLogger(__name__)
@ -36,7 +37,7 @@ class K8sAPI(apiv_api.ApivApi):
tmp.write(content)
tmp.flush()
except Exception as err:
LOG.error("Error while creating temp file: %s", err)
LOG.error(_LE("Error while creating temp file: %s"), err)
raise
return tmp

View File

@ -15,6 +15,8 @@
import re
import pep8
"""
Guidelines for writing new hacking checks
@ -56,6 +58,14 @@ assert_true_isinstance_re = re.compile(
dict_constructor_with_list_copy_re = re.compile(r".*\bdict\((\[)?(\(|\[)")
assert_xrange_re = re.compile(
r"\s*xrange\s*\(")
log_translation = re.compile(
r"(.)*LOG\.(audit|error|critical)\(\s*('|\")")
log_translation_info = re.compile(
r"(.)*LOG\.(info)\(\s*(_\(|'|\")")
log_translation_exception = re.compile(
r"(.)*LOG\.(exception)\(\s*(_\(|'|\")")
log_translation_LW = re.compile(
r"(.)*LOG\.(warning|warn)\(\s*(_\(|'|\")")
custom_underscore_check = re.compile(r"(.)*_\s*=\s*(.)*")
underscore_import_check = re.compile(r"(.)*import _(.)*")
translated_log = re.compile(
@ -125,6 +135,23 @@ def assert_true_isinstance(logical_line):
yield (0, "M316: assertTrue(isinstance(a, b)) sentences not allowed")
def validate_log_translations(logical_line, physical_line, filename=None):
if pep8.noqa(physical_line):
return
msg = "M328: LOG.info messages require translations `_LI()`!"
if log_translation_info.match(logical_line):
yield (0, msg)
msg = "M329: LOG.exception messages require translations `_LE()`!"
if log_translation_exception.match(logical_line):
yield (0, msg)
msg = "M330: LOG.warning, LOG.warn messages require translations `_LW()`!"
if log_translation_LW.match(logical_line):
yield (0, msg)
msg = "M321: Log messages require translations!"
if log_translation.match(logical_line):
yield (0, msg)
def assert_equal_in(logical_line):
"""Check for assertEqual(True|False, A in B), assertEqual(A in B, True|False)
@ -215,4 +242,5 @@ def factory(register):
register(dict_constructor_with_list_copy)
register(no_xrange)
register(no_log_warn)
register(validate_log_translations)
register(check_explicit_underscore_import)

View File

@ -132,9 +132,10 @@ class MagnumPeriodicTasks(periodic_task.PeriodicTasks):
# Any other exception means we do not perform any
# action on this bay in the current sync run, so remove
# it from all records.
LOG.warning("Exception while attempting to retrieve "
"Heat stack %s for bay %s. Traceback "
"follows.")
LOG.warning(_LW("Exception while attempting to retrieve "
"Heat stack %(stack_id)s for bay %(bay_id)s. "
"Traceback follows."),
{'stack_id': bay.stack_id, 'bay_id': bay.id})
LOG.warning(e)
_sid_to_bay_mapping.pop(bay.stack_id)
_bay_stack_ids.remove(bay.stack_id)

View File

@ -13,6 +13,9 @@
from oslo_log import log as logging
from tempest.lib import exceptions
from magnum.i18n import _LE
from magnum.i18n import _LI
from magnum.i18n import _LW
from magnum.tests.functional.api.v1.models import bay_model
from magnum.tests.functional.common import client
from magnum.tests.functional.common import utils
@ -120,9 +123,9 @@ class BayClient(client.MagnumClient):
lambda: self.does_bay_exist(bay_id), 10, 1800)
except Exception:
# In error state. Clean up the bay id if desired
self.LOG.error('Bay %s entered an exception state.' % bay_id)
self.LOG.error(_LE('Bay %s entered an exception state.') % bay_id)
if delete_on_error:
self.LOG.error('We will attempt to delete bays now.')
self.LOG.error(_LE('We will attempt to delete bays now.'))
self.delete_bay(bay_id)
self.wait_for_bay_to_delete(bay_id)
raise
@ -136,35 +139,35 @@ class BayClient(client.MagnumClient):
resp, model = self.get_bay(bay_id)
if model.status in ['CREATED', 'CREATE_COMPLETE',
'ERROR', 'CREATE_FAILED']:
self.LOG.info('Bay %s succeeded.' % bay_id)
self.LOG.info(_LI('Bay %s succeeded.') % bay_id)
return True
else:
return False
except exceptions.NotFound:
self.LOG.warning('Bay %s is not found.' % bay_id)
self.LOG.warning(_LW('Bay %s is not found.') % bay_id)
return False
def does_bay_exist(self, bay_id):
try:
resp, model = self.get_bay(bay_id)
if model.status in ['CREATED', 'CREATE_COMPLETE']:
self.LOG.info('Bay %s is created.' % bay_id)
self.LOG.info(_LI('Bay %s is created.') % bay_id)
return True
elif model.status in ['ERROR', 'CREATE_FAILED']:
self.LOG.error('Bay %s is in fail state.' % bay_id)
self.LOG.error(_LE('Bay %s is in fail state.') % bay_id)
raise exceptions.ServerFault(
"Got into an error condition: %s for %s" %
(model.status, bay_id))
else:
return False
except exceptions.NotFound:
self.LOG.warning('Bay %s is not found.' % bay_id)
self.LOG.warning(_LW('Bay %s is not found.') % bay_id)
return False
def does_bay_not_exist(self, bay_id):
try:
self.get_bay(bay_id)
except exceptions.NotFound:
self.LOG.warning('Bay %s is not found.' % bay_id)
self.LOG.warning(_LW('Bay %s is not found.') % bay_id)
return True
return False

View File

@ -17,6 +17,8 @@ import subprocess
from tempest.lib import base
import magnum
from magnum.i18n import _LE
from magnum.i18n import _LI
COPY_LOG_HELPER = "magnum/tests/contrib/copy_instance_logs.sh"
@ -44,10 +46,10 @@ class BaseMagnumTest(base.BaseTestCase):
"""
def int_copy_logs(exec_info):
try:
cls.LOG.info("Copying logs...")
cls.LOG.info(_LI("Copying logs..."))
fn = exec_info[2].tb_frame.f_locals['fn']
func_name = fn.im_self._get_test_method().__name__
msg = "Failed to copy logs for bay"
msg = (_LE("Failed to copy logs for bay"))
nodes_addresses = get_nodes_fn()
master_nodes = nodes_addresses[0]
@ -61,8 +63,8 @@ class BaseMagnumTest(base.BaseTestCase):
if not nodes_address:
return
cls.LOG.info("copy logs from : %s" %
','.join(nodes_address))
msg = _LI("copy logs from : %s") % ','.join(nodes_address)
cls.LOG.info(msg)
log_name = prefix + "-" + func_name
for node_address in nodes_address:
try:
@ -77,10 +79,13 @@ class BaseMagnumTest(base.BaseTestCase):
])
except Exception:
cls.LOG.error(msg)
cls.LOG.exception(
"failed to copy from %s to %s%s-%s" %
(node_address, "/opt/stack/logs/bay-nodes/",
log_name, node_address))
msg = (_LE("failed to copy from %{node_address}s "
"to %{base_path}s%{log_name}s-"
"%{node_address}s") %
{'node_address': node_address,
'base_path': "/opt/stack/logs/bay-nodes/",
'log_name': log_name})
cls.LOG.exception(msg)
do_copy_logs('master', master_nodes)
do_copy_logs('node', agent_nodes)

View File

@ -31,6 +31,7 @@ from k8sclient.client import api_client
from k8sclient.client.apis import apiv_api
from keystoneclient.v2_0 import client as ksclient
from magnum.common.utils import rmtree_without_raise
from magnum.i18n import _LI
from magnum.tests.functional.common import base
from magnum.tests.functional.common import utils
from magnumclient.common.apiclient import exceptions
@ -275,11 +276,11 @@ extendedKeyUsage = clientAuth
def _get_nodes(self):
nodes = self._get_nodes_from_bay()
if not [x for x in nodes if x]:
self.LOG.info("the list of nodes from bay is empty")
self.LOG.info(_LI("the list of nodes from bay is empty"))
nodes = self._get_nodes_from_stack()
if not [x for x in nodes if x]:
self.LOG.info("the list of nodes from stack is empty")
self.LOG.info("Nodes are: %s" % nodes)
self.LOG.info(_LI("the list of nodes from stack is empty"))
self.LOG.info(_LI("Nodes are: %s") % nodes)
return nodes
def _get_nodes_from_bay(self):
@ -378,10 +379,10 @@ class BaseK8sTest(BayTest):
def _is_api_ready(self):
try:
self.k8s_api.list_namespaced_node()
self.LOG.info("API is ready.")
self.LOG.info(_LI("API is ready."))
return True
except Exception:
self.LOG.info("API is not ready yet.")
self.LOG.info(_LI("API is not ready yet."))
return False
def test_pod_apis(self):

View File

@ -17,6 +17,7 @@ from oslo_config import cfg
from requests import exceptions as req_exceptions
from magnum.common import docker_utils
from magnum.i18n import _LI
from magnum.tests.functional.python_client_base import BayTest
@ -97,15 +98,15 @@ class TestSwarmAPIs(BayTest):
# investigate the cause of this issue. See bug #1583337.
for i in range(150):
try:
self.LOG.info("Calling function " + func.__name__)
self.LOG.info(_LI("Calling function ") + func.__name__)
return func(*args, **kwargs)
except req_exceptions.ConnectionError:
self.LOG.info("Connection aborted on calling Swarm API. "
"Will retry in 2 seconds.")
self.LOG.info(_LI("Connection aborted on calling Swarm API. "
"Will retry in 2 seconds."))
except errors.APIError as e:
if e.response.status_code != 500:
raise
self.LOG.info("Internal Server Error: " + str(e))
self.LOG.info(_LI("Internal Server Error: ") + str(e))
time.sleep(2)
raise Exception("Cannot connect to Swarm API.")

View File

@ -215,6 +215,28 @@ class HackingTestCase(base.TestCase):
"""
self._assert_has_no_errors(code, check)
def test_log_translations(self):
logs = ['audit', 'error', 'info', 'warning', 'critical', 'warn',
'exception']
levels = ['_LI', '_LW', '_LE', '_LC']
debug = "LOG.debug('OK')"
self.assertEqual(
0, len(list(checks.validate_log_translations(debug, debug))))
for log in logs:
bad = 'LOG.%s("Bad")' % log
self.assertEqual(
1, len(list(checks.validate_log_translations(bad, bad))))
ok = "LOG.%s('OK') # noqa" % log
self.assertEqual(
0, len(list(checks.validate_log_translations(ok, ok))))
ok = "LOG.%s(variable)" % log
self.assertEqual(
0, len(list(checks.validate_log_translations(ok, ok))))
for level in levels:
ok = "LOG.%s(%s('OK'))" % (log, level)
self.assertEqual(
0, len(list(checks.validate_log_translations(ok, ok))))
def test_use_timeunitls_utcow(self):
errors = [(1, 0, "M310")]
check = checks.use_timeutils_utcnow