NetApp ONTAP: Add support for filtering API tracing
API Tracing is valuable when diagnosing problems or unexpected behaviors with the ONTAP Manila driver. However, turning it on may spam logs and make it rather harder to trace through specific API calls. Change-Id: I3c91638138ec1a2652efdadbc86176afa295abd8
This commit is contained in:
parent
9f622e2a50
commit
148f77f184
@ -19,6 +19,7 @@ Contains classes required to issue API calls to Data ONTAP and OnCommand DFM.
|
||||
"""
|
||||
|
||||
import copy
|
||||
import re
|
||||
|
||||
from lxml import etree
|
||||
from oslo_log import log
|
||||
@ -27,6 +28,7 @@ from six.moves import urllib
|
||||
|
||||
from manila import exception
|
||||
from manila.i18n import _
|
||||
from manila.share.drivers.netapp import utils
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
@ -69,7 +71,8 @@ class NaServer(object):
|
||||
def __init__(self, host, server_type=SERVER_TYPE_FILER,
|
||||
transport_type=TRANSPORT_TYPE_HTTP,
|
||||
style=STYLE_LOGIN_PASSWORD, username=None,
|
||||
password=None, port=None, trace=False):
|
||||
password=None, port=None, trace=False,
|
||||
api_trace_pattern=utils.API_TRACE_PATTERN):
|
||||
self._host = host
|
||||
self.set_server_type(server_type)
|
||||
self.set_transport_type(transport_type)
|
||||
@ -79,8 +82,8 @@ class NaServer(object):
|
||||
self._username = username
|
||||
self._password = password
|
||||
self._trace = trace
|
||||
self._api_trace_pattern = api_trace_pattern
|
||||
self._refresh_conn = True
|
||||
self._trace = trace
|
||||
|
||||
LOG.debug('Using NetApp controller: %s', self._host)
|
||||
|
||||
@ -213,19 +216,18 @@ class NaServer(object):
|
||||
self._password = password
|
||||
self._refresh_conn = True
|
||||
|
||||
def set_trace(self, trace=True):
|
||||
"""Enable or disable the API tracing facility."""
|
||||
self._trace = trace
|
||||
|
||||
def invoke_elem(self, na_element, enable_tunneling=False):
|
||||
"""Invoke the API on the server."""
|
||||
if na_element and not isinstance(na_element, NaElement):
|
||||
ValueError('NaElement must be supplied to invoke API')
|
||||
|
||||
request, request_element = self._create_request(na_element,
|
||||
enable_tunneling)
|
||||
|
||||
if self._trace:
|
||||
api_name = na_element.get_name()
|
||||
api_name_matches_regex = (re.match(self._api_trace_pattern, api_name)
|
||||
is not None)
|
||||
|
||||
if self._trace and api_name_matches_regex:
|
||||
LOG.debug("Request: %s", request_element.to_string(pretty=True))
|
||||
|
||||
if (not hasattr(self, '_opener') or not self._opener
|
||||
@ -246,7 +248,7 @@ class NaServer(object):
|
||||
response_xml = response.read()
|
||||
response_element = self._get_result(response_xml)
|
||||
|
||||
if self._trace:
|
||||
if self._trace and api_name_matches_regex:
|
||||
LOG.debug("Response: %s", response_element.to_string(pretty=True))
|
||||
|
||||
return response_element
|
||||
|
@ -32,7 +32,9 @@ class NetAppBaseClient(object):
|
||||
port=kwargs['port'],
|
||||
username=kwargs['username'],
|
||||
password=kwargs['password'],
|
||||
trace=kwargs.get('trace', False))
|
||||
trace=kwargs.get('trace', False),
|
||||
api_trace_pattern=kwargs.get('api_trace_pattern',
|
||||
na_utils.API_TRACE_PATTERN))
|
||||
|
||||
def get_ontapi_version(self, cached=True):
|
||||
"""Gets the supported ontapi version."""
|
||||
|
@ -117,7 +117,8 @@ class NetAppCmodeFileStorageLibrary(object):
|
||||
|
||||
self._app_version = kwargs.get('app_version', 'unknown')
|
||||
|
||||
na_utils.setup_tracing(self.configuration.netapp_trace_flags)
|
||||
na_utils.setup_tracing(self.configuration.netapp_trace_flags,
|
||||
self.configuration.netapp_api_trace_pattern)
|
||||
self._backend_name = self.configuration.safe_get(
|
||||
'share_backend_name') or driver_name
|
||||
|
||||
@ -150,7 +151,8 @@ class NetAppCmodeFileStorageLibrary(object):
|
||||
hostname=self.configuration.netapp_server_hostname,
|
||||
port=self.configuration.netapp_server_port,
|
||||
vserver=vserver,
|
||||
trace=na_utils.TRACE_API)
|
||||
trace=na_utils.TRACE_API,
|
||||
api_trace_pattern=na_utils.API_TRACE_PATTERN)
|
||||
self._clients[vserver] = client
|
||||
|
||||
return client
|
||||
|
@ -115,7 +115,16 @@ netapp_support_opts = [
|
||||
cfg.StrOpt('netapp_trace_flags',
|
||||
help=('Comma-separated list of options that control which '
|
||||
'trace info is written to the debug logs. Values '
|
||||
'include method and api.')), ]
|
||||
'include method and api. API logging can further be '
|
||||
'filtered with the '
|
||||
'``netapp_api_trace_pattern option``.')),
|
||||
cfg.StrOpt('netapp_api_trace_pattern',
|
||||
default='(.*)',
|
||||
help=('A regular expression to limit the API tracing. This '
|
||||
'option is honored only if enabling ``api`` tracing '
|
||||
'with the ``netapp_trace_flags`` option. By default, '
|
||||
'all APIs will be traced.')),
|
||||
]
|
||||
|
||||
netapp_data_motion_opts = [
|
||||
cfg.IntOpt('netapp_snapmirror_quiesce_timeout',
|
||||
|
@ -18,6 +18,7 @@
|
||||
import collections
|
||||
import decimal
|
||||
import platform
|
||||
import re
|
||||
|
||||
from oslo_concurrency import processutils as putils
|
||||
from oslo_log import log
|
||||
@ -33,6 +34,7 @@ LOG = log.getLogger(__name__)
|
||||
VALID_TRACE_FLAGS = ['method', 'api']
|
||||
TRACE_METHOD = False
|
||||
TRACE_API = False
|
||||
API_TRACE_PATTERN = '(.*)'
|
||||
|
||||
|
||||
def validate_driver_instantiation(**kwargs):
|
||||
@ -65,16 +67,24 @@ def round_down(value, precision='0.00'):
|
||||
decimal.Decimal(precision), rounding=decimal.ROUND_DOWN))
|
||||
|
||||
|
||||
def setup_tracing(trace_flags_string):
|
||||
def setup_tracing(trace_flags_string, api_trace_pattern=API_TRACE_PATTERN):
|
||||
global TRACE_METHOD
|
||||
global TRACE_API
|
||||
global API_TRACE_PATTERN
|
||||
TRACE_METHOD = False
|
||||
TRACE_API = False
|
||||
API_TRACE_PATTERN = api_trace_pattern
|
||||
if trace_flags_string:
|
||||
flags = trace_flags_string.split(',')
|
||||
flags = [flag.strip() for flag in flags]
|
||||
for invalid_flag in list(set(flags) - set(VALID_TRACE_FLAGS)):
|
||||
LOG.warning('Invalid trace flag: %s', invalid_flag)
|
||||
try:
|
||||
re.compile(api_trace_pattern)
|
||||
except re.error:
|
||||
msg = _('Cannot parse the API trace pattern. %s is not a '
|
||||
'valid python regular expression.') % api_trace_pattern
|
||||
raise exception.BadConfigurationException(reason=msg)
|
||||
TRACE_METHOD = 'method' in flags
|
||||
TRACE_API = 'api' in flags
|
||||
|
||||
|
@ -24,7 +24,8 @@ CONNECTION_INFO = {
|
||||
'transport_type': 'https',
|
||||
'port': 443,
|
||||
'username': 'admin',
|
||||
'password': 'passw0rd'
|
||||
'password': 'passw0rd',
|
||||
'api_trace_pattern': '(.*)',
|
||||
}
|
||||
|
||||
CLUSTER_NAME = 'fake_cluster'
|
||||
@ -2395,7 +2396,7 @@ QOS_POLICY_GROUP_GET_ITER_RESPONSE = etree.XML("""
|
||||
'max_througput': QOS_MAX_THROUGHPUT,
|
||||
})
|
||||
|
||||
FAKE_VOL_XML = """<volume-info xmlns='http://www.netapp.com/filer/admin'>
|
||||
FAKE_VOL_XML = """<volume-info>
|
||||
<name>open123</name>
|
||||
<state>online</state>
|
||||
<size-total>0</size-total>
|
||||
|
@ -220,10 +220,20 @@ class NetAppApiServerTests(test.TestCase):
|
||||
na_element)
|
||||
self.assertEqual('unknown', exception.code)
|
||||
|
||||
def test_invoke_elem_valid(self):
|
||||
@ddt.data({'trace_enabled': False,
|
||||
'trace_pattern': '(.*)', 'log': False},
|
||||
{'trace_enabled': True,
|
||||
'trace_pattern': '(?!(volume)).*', 'log': False},
|
||||
{'trace_enabled': True,
|
||||
'trace_pattern': '(.*)', 'log': True},
|
||||
{'trace_enabled': True,
|
||||
'trace_pattern': '^volume-(info|get-iter)$', 'log': True})
|
||||
@ddt.unpack
|
||||
def test_invoke_elem_valid(self, trace_enabled, trace_pattern, log):
|
||||
"""Tests the method invoke_elem with valid parameters"""
|
||||
na_element = fake.FAKE_NA_ELEMENT
|
||||
self.root._trace = True
|
||||
self.root._trace = trace_enabled
|
||||
self.root._api_trace_pattern = trace_pattern
|
||||
self.mock_object(self.root, '_create_request', mock.Mock(
|
||||
return_value=('abc', fake.FAKE_NA_ELEMENT)))
|
||||
self.mock_object(api, 'LOG')
|
||||
@ -237,4 +247,5 @@ class NetAppApiServerTests(test.TestCase):
|
||||
|
||||
self.root.invoke_elem(na_element)
|
||||
|
||||
self.assertEqual(2, api.LOG.debug.call_count)
|
||||
expected_log_count = 2 if log else 0
|
||||
self.assertEqual(expected_log_count, api.LOG.debug.call_count)
|
||||
|
@ -83,7 +83,8 @@ CLIENT_KWARGS = {
|
||||
'vserver': None,
|
||||
'transport_type': 'https',
|
||||
'password': 'pass',
|
||||
'port': '443'
|
||||
'port': '443',
|
||||
'api_trace_pattern': '(.*)',
|
||||
}
|
||||
|
||||
SHARE = {
|
||||
|
@ -18,6 +18,7 @@ Mock unit tests for the NetApp driver utility module
|
||||
|
||||
import platform
|
||||
|
||||
import ddt
|
||||
import mock
|
||||
from oslo_concurrency import processutils as putils
|
||||
from oslo_log import log
|
||||
@ -28,6 +29,7 @@ from manila import test
|
||||
from manila import version
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class NetAppDriverUtilsTestCase(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
@ -56,19 +58,22 @@ class NetAppDriverUtilsTestCase(test.TestCase):
|
||||
self.assertAlmostEqual(na_utils.round_down(-5.567, '0'), -5)
|
||||
|
||||
def test_setup_tracing(self):
|
||||
na_utils.setup_tracing(None)
|
||||
na_utils.setup_tracing(None, api_trace_pattern='(.*)')
|
||||
self.assertFalse(na_utils.TRACE_API)
|
||||
self.assertFalse(na_utils.TRACE_METHOD)
|
||||
self.assertEqual('(.*)', na_utils.API_TRACE_PATTERN)
|
||||
self.assertEqual(0, na_utils.LOG.warning.call_count)
|
||||
|
||||
na_utils.setup_tracing('method')
|
||||
self.assertFalse(na_utils.TRACE_API)
|
||||
self.assertTrue(na_utils.TRACE_METHOD)
|
||||
self.assertEqual('(.*)', na_utils.API_TRACE_PATTERN)
|
||||
self.assertEqual(0, na_utils.LOG.warning.call_count)
|
||||
|
||||
na_utils.setup_tracing('method,api')
|
||||
na_utils.setup_tracing('method,api', api_trace_pattern='(^fancy-api$)')
|
||||
self.assertTrue(na_utils.TRACE_API)
|
||||
self.assertTrue(na_utils.TRACE_METHOD)
|
||||
self.assertEqual('(^fancy-api$)', na_utils.API_TRACE_PATTERN)
|
||||
self.assertEqual(0, na_utils.LOG.warning.call_count)
|
||||
|
||||
def test_setup_tracing_invalid_key(self):
|
||||
@ -78,6 +83,12 @@ class NetAppDriverUtilsTestCase(test.TestCase):
|
||||
self.assertTrue(na_utils.TRACE_METHOD)
|
||||
self.assertEqual(1, na_utils.LOG.warning.call_count)
|
||||
|
||||
@ddt.data('?!(bad', '(reg]+', 'eX?!)')
|
||||
def test_setup_tracing_invalid_regex(self, regex):
|
||||
self.assertRaises(exception.BadConfigurationException,
|
||||
na_utils.setup_tracing, 'method,api',
|
||||
api_trace_pattern=regex)
|
||||
|
||||
@na_utils.trace
|
||||
def _trace_test_method(*args, **kwargs):
|
||||
return 'OK'
|
||||
|
@ -0,0 +1,6 @@
|
||||
---
|
||||
features:
|
||||
- The NetApp driver supports a new configuration option
|
||||
``netapp_api_trace_pattern`` to enable filtering backend API
|
||||
interactions to log. This option must be specified in the backend
|
||||
section when desired and it accepts a valid python regular expression.
|
Loading…
x
Reference in New Issue
Block a user