Merge "Add log retrieval to Cassandra"

This commit is contained in:
Jenkins 2016-08-18 17:25:39 +00:00 committed by Gerrit Code Review
commit 754a2dee7a
10 changed files with 127 additions and 2 deletions

View File

@ -0,0 +1,3 @@
---
features:
- Enable database log retrieval on Cassandra instances.

View File

@ -44,3 +44,4 @@ osprofiler>=1.3.0 # Apache-2.0
oslo.log>=1.14.0 # Apache-2.0
oslo.db>=4.10.0 # Apache-2.0
enum34;python_version=='2.7' or python_version=='2.6' or python_version=='3.3' # BSD
xmltodict>=0.10.1 # MIT

View File

@ -862,8 +862,12 @@ cassandra_opts = [
cfg.ListOpt('ignore_dbs', default=['system', 'system_auth',
'system_traces'],
help='Databases to exclude when listing databases.'),
cfg.StrOpt('guest_log_exposed_logs', default='',
cfg.StrOpt('guest_log_exposed_logs', default='system',
help='List of Guest Logs to expose for publishing.'),
cfg.StrOpt('system_log_level',
choices=['ALL', 'TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR'],
default='INFO',
help='Cassandra log verbosity.'),
cfg.BoolOpt('cluster_support', default=True,
help='Enable clusters to be created and managed.'),
cfg.StrOpt('api_strategy',

View File

@ -21,6 +21,7 @@ import json
import re
import six
from six.moves.configparser import SafeConfigParser
import xmltodict
import yaml
@ -427,3 +428,16 @@ class Base64Codec(StreamCodec):
# py27 & py34 seem to understand bytearray the same
return bytearray([item for item in base64.b64decode(stream)])
class XmlCodec(StreamCodec):
def __init__(self, encoding='utf-8'):
self._encoding = encoding
def serialize(self, dict_data):
return xmltodict.unparse(
dict_data, output=None, encoding=self._encoding, pretty=True)
def deserialize(self, stream):
return xmltodict.parse(stream, encoding=self._encoding)

View File

@ -25,6 +25,7 @@ from trove.common.notification import EndNotification
from trove.guestagent import backup
from trove.guestagent.datastore.experimental.cassandra import service
from trove.guestagent.datastore import manager
from trove.guestagent import guest_log
from trove.guestagent import volume
@ -34,6 +35,8 @@ CONF = cfg.CONF
class Manager(manager.Manager):
GUEST_LOG_DEFS_SYSTEM_LABEL = 'system'
def __init__(self, manager_name='cassandra'):
super(Manager, self).__init__(manager_name)
self._app = None
@ -62,6 +65,29 @@ class Manager(manager.Manager):
def configuration_manager(self):
return self.app.configuration_manager
@property
def datastore_log_defs(self):
system_log_file = self.validate_log_file(
self.app.cassandra_system_log_file, self.app.cassandra_owner)
return {
self.GUEST_LOG_DEFS_SYSTEM_LABEL: {
self.GUEST_LOG_TYPE_LABEL: guest_log.LogType.USER,
self.GUEST_LOG_USER_LABEL: self.app.cassandra_owner,
self.GUEST_LOG_FILE_LABEL: system_log_file
}
}
def guest_log_enable(self, context, log_name, disable):
if disable:
LOG.debug("Disabling system log.")
self.app.set_logging_level('OFF')
else:
log_level = CONF.get(self.manager_name).get('system_log_level')
LOG.debug("Enabling system log with logging level: %s" % log_level)
self.app.set_logging_level(log_level)
return False
def restart(self, context):
self.app.restart()

View File

@ -32,6 +32,7 @@ from trove.common import instance as rd_instance
from trove.common.stream_codecs import IniCodec
from trove.common.stream_codecs import PropertiesCodec
from trove.common.stream_codecs import SafeYamlCodec
from trove.common.stream_codecs import XmlCodec
from trove.common import utils
from trove.guestagent.common.configuration import ConfigurationManager
from trove.guestagent.common.configuration import OneFileOverrideStrategy
@ -62,6 +63,7 @@ class CassandraApp(object):
CASSANDRA_CONF_FILE = "cassandra.yaml"
CASSANDRA_TOPOLOGY_FILE = 'cassandra-rackdc.properties'
CASSANDRA_LOGBACK_FILE = "logback.xml"
_TOPOLOGY_CODEC = PropertiesCodec(
delimiter='=', unpack_singletons=True, string_mappings={
@ -82,6 +84,14 @@ class CassandraApp(object):
SafeYamlCodec(default_flow_style=False), requires_root=True,
override_strategy=OneFileOverrideStrategy(revision_dir))
lb_revision_dir = guestagent_utils.build_file_path(
os.path.dirname(self.cassandra_logback), 'logback-overrides')
self.logback_conf_manager = ConfigurationManager(
self.cassandra_logback,
self.cassandra_owner, self.cassandra_owner,
XmlCodec(), requires_root=True,
override_strategy=OneFileOverrideStrategy(lb_revision_dir))
@property
def service_candidates(self):
return ['cassandra']
@ -117,6 +127,20 @@ class CassandraApp(object):
def cassandra_working_dir(self):
return "/var/lib/cassandra"
@property
def cassandra_system_log_file(self):
return guestagent_utils.build_file_path(
self.cassandra_log_dir, 'system', 'log')
@property
def cassandra_log_dir(self):
return "/var/log/cassandra"
@property
def cassandra_logback(self):
return guestagent_utils.build_file_path(self.cassandra_conf_dir,
self.CASSANDRA_LOGBACK_FILE)
@property
def default_superuser_name(self):
return "cassandra"
@ -686,6 +710,16 @@ class CassandraApp(object):
# <keyspace> ( <table> ... )
self._run_nodetool_command('flush', keyspace, *tables)
def set_logging_level(self, log_level):
"""Set the log Cassandra's system log verbosity level.
"""
# Apply the change at runtime.
self._run_nodetool_command('setlogginglevel', 'root', log_level)
# Persist the change.
self.logback_conf_manager.apply_system_override(
{'configuration': {'root': {'@level': log_level}}})
def _run_nodetool_command(self, cmd, *args, **kwargs):
"""Execute a nodetool command on this node.
"""

View File

@ -157,3 +157,6 @@ class CassandraHelper(TestHelper):
def get_invalid_groups(self):
return [{'sstable_preemptive_open_interval_in_mb': -1},
{'sstable_preemptive_open_interval_in_mb': 'string_value'}]
def get_exposed_user_log_names(self):
return ['system']

View File

@ -674,3 +674,12 @@ class GuestLogRunner(TestRunner):
expected_type=guest_log.LogType.SYS.name,
expected_status=guest_log.LogStatus.Ready.name,
expected_published=0, expected_pending=1)
class CassandraGuestLogRunner(GuestLogRunner):
def run_test_log_show(self):
self.assert_log_show(self.auth_client,
self._get_exposed_user_log_name(),
expected_published=0,
expected_pending=None)

View File

@ -775,3 +775,22 @@ class GuestAgentCassandraDBManagerTest(DatastoreManagerTest):
'list_superusers',
return_value=[trove_admin, other_admin]):
self.assertTrue(self.manager.is_root_enabled(self.context))
def test_guest_log_enable(self):
self._assert_guest_log_enable(False, 'INFO')
self._assert_guest_log_enable(True, 'OFF')
def _assert_guest_log_enable(self, disable, expected_level):
with patch.multiple(
self.manager._app,
logback_conf_manager=DEFAULT,
_run_nodetool_command=DEFAULT
) as app_mocks:
self.assertFalse(self.manager.guest_log_enable(
Mock(), Mock(), disable))
(app_mocks['logback_conf_manager'].apply_system_override.
assert_called_once_with(
{'configuration': {'root': {'@level': expected_level}}}))
app_mocks['_run_nodetool_command'].assert_called_once_with(
'setlogginglevel', 'root', expected_level)

View File

@ -26,7 +26,7 @@ from testtools import ExpectedException
from trove.common import exception
from trove.common.stream_codecs import (
Base64Codec, IdentityCodec, IniCodec, JsonCodec,
KeyValueCodec, PropertiesCodec, YamlCodec)
KeyValueCodec, PropertiesCodec, XmlCodec, YamlCodec)
from trove.common import utils
from trove.guestagent.common import guestagent_utils
from trove.guestagent.common import operating_system
@ -132,6 +132,18 @@ class TestOperatingSystem(trove_testtools.TestCase):
self._test_file_codec(data, JsonCodec())
def test_xml_file_codec(self):
data = {'document': {'@name': 'mydocument', '@ttl': '10',
'author': {'@name': 'Jycll ;-)'},
'page': [{'@number': '1', 'paragraph':
['lorem ipsum', 'more lorem ipsum']},
{'@number': '1', 'paragraph':
['lorem ipsum', 'more lorem ipsum']}]
}
}
self._test_file_codec(data, XmlCodec())
def _test_file_codec(self, data, read_codec, write_codec=None,
expected_data=None,
expected_exception=None,