add trace_requests option to debug section

this supports the use case that David Kranz has in tracing
specific tests for test development. Because this is expected to
be development only, and not used in a general case, we don't do
all the md5 niceties of the previous code.

Change-Id: Ia9c7a7d10456d0583f8897111b958050c8e95ed7
diff --git a/etc/tempest.conf.sample b/etc/tempest.conf.sample
index c4dad7e..2fdbb7e 100644
--- a/etc/tempest.conf.sample
+++ b/etc/tempest.conf.sample
@@ -421,6 +421,22 @@
 # Enable diagnostic commands (boolean value)
 #enable=true
 
+# A regex to determine which requests should be traced.  This
+# is a regex to match the caller for rest client requests to
+# be able to selectively trace calls out of specific classes
+# and methods. It largely exists for test development, and is
+# not expected to be used in a real deploy of tempest. This
+# will be matched against the discovered ClassName:method in
+# the test environment.  Expected values for this field are:
+# * ClassName:test_method_name - traces one test_method  *
+# ClassName:setUp(Class) - traces specific setup functions  *
+# ClassName:tearDown(Class) - traces specific teardown
+# functions  * ClassName:_run_cleanups - traces the cleanup
+# functions  If nothing is specified, this feature is not
+# enabled. To trace everything specify .* as the regex.
+# (string value)
+#trace_requests=
+
 
 [identity]
 
diff --git a/tempest/common/rest_client.py b/tempest/common/rest_client.py
index 35d4ff2..830968e 100644
--- a/tempest/common/rest_client.py
+++ b/tempest/common/rest_client.py
@@ -281,24 +281,50 @@
                 return resp[i]
         return ""
 
-    def _log_request(self, method, req_url, resp, secs=""):
+    def _log_request(self, method, req_url, resp,
+                     secs="", req_headers=None,
+                     req_body=None, resp_body=None):
         # if we have the request id, put it in the right part of the log
         extra = dict(request_id=self._get_request_id(resp))
         # NOTE(sdague): while we still have 6 callers to this function
         # we're going to just provide work around on who is actually
         # providing timings by gracefully adding no content if they don't.
         # Once we're down to 1 caller, clean this up.
+        caller_name = self._find_caller()
         if secs:
             secs = " %.3fs" % secs
         self.LOG.info(
             'Request (%s): %s %s %s%s' % (
-                self._find_caller(),
+                caller_name,
                 resp['status'],
                 method,
                 req_url,
                 secs),
             extra=extra)
 
+        # We intentionally duplicate the info content because in a parallel
+        # world this is important to match
+        trace_regex = CONF.debug.trace_requests
+        if trace_regex and re.search(trace_regex, caller_name):
+            log_fmt = """Request (%s): %s %s %s%s
+    Request - Headers: %s
+        Body: %s
+    Response - Headers: %s
+        Body: %s"""
+
+            self.LOG.debug(
+                log_fmt % (
+                    caller_name,
+                    resp['status'],
+                    method,
+                    req_url,
+                    secs,
+                    str(req_headers),
+                    str(req_body)[:2048],
+                    str(resp),
+                    str(resp_body)[:2048]),
+                extra=extra)
+
     def _parse_resp(self, body):
         if self._get_type() is "json":
             body = json.loads(body)
@@ -382,7 +408,10 @@
         resp, resp_body = self.http_obj.request(
             req_url, method, headers=req_headers, body=req_body)
         end = time.time()
-        self._log_request(method, req_url, resp, secs=(end - start))
+        self._log_request(method, req_url, resp, secs=(end - start),
+                          req_headers=req_headers, req_body=req_body,
+                          resp_body=resp_body)
+
         # Verify HTTP response codes
         self.response_checker(method, url, req_headers, req_body, resp,
                               resp_body)
diff --git a/tempest/config.py b/tempest/config.py
index af96132..0212d8a 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -794,6 +794,26 @@
     cfg.BoolOpt('enable',
                 default=True,
                 help="Enable diagnostic commands"),
+    cfg.StrOpt('trace_requests',
+               default='',
+               help="""A regex to determine which requests should be traced.
+
+This is a regex to match the caller for rest client requests to be able to
+selectively trace calls out of specific classes and methods. It largely
+exists for test development, and is not expected to be used in a real deploy
+of tempest. This will be matched against the discovered ClassName:method
+in the test environment.
+
+Expected values for this field are:
+
+ * ClassName:test_method_name - traces one test_method
+ * ClassName:setUp(Class) - traces specific setup functions
+ * ClassName:tearDown(Class) - traces specific teardown functions
+ * ClassName:_run_cleanups - traces the cleanup functions
+
+If nothing is specified, this feature is not enabled. To trace everything
+specify .* as the regex.
+""")
 ]
 
 input_scenario_group = cfg.OptGroup(name="input-scenario",