=== modified file 'lava/dispatcher/commands.py'
@@ -53,6 +53,10 @@
type=int,
help="Used internally by LAVA scheduler.")
parser.add_argument(
+ "--output-dir",
+ default=None,
+ help="Directory to put structured output in.")
+ parser.add_argument(
"--validate", action='store_true',
help="Just validate the job file, do not execute any steps.")
parser.add_argument(
@@ -111,7 +115,9 @@
else:
json_jobdata['target'] = self.args.target
jobdata = json.dumps(json_jobdata)
- job = LavaTestJob(jobdata, oob_file, config)
+ if self.args.output_dir and not os.path.isdir(self.args.output_dir):
+ os.makedirs(self.args.output_dir)
+ job = LavaTestJob(jobdata, oob_file, config, self.args.output_dir)
#FIXME Return status
if self.args.validate:
=== modified file 'lava_dispatcher/actions/launch_control.py'
@@ -236,6 +236,7 @@
try:
result = dashboard.put_ex(json_bundle, job_name, stream)
print >> self.context.oob_file, 'dashboard-put-result:', result
+ self.context.output.write_named_data('result-bundle', result)
logging.info("Dashboard : %s" % result)
except xmlrpclib.Fault, err:
logging.warning("xmlrpclib.Fault occurred")
=== modified file 'lava_dispatcher/context.py'
@@ -19,6 +19,7 @@
# with this program; if not, see <http://www.gnu.org/licenses>.
import atexit
+import logging
import os
import sys
import tempfile
@@ -29,14 +30,66 @@
from lava_dispatcher.utils import rmtree
+def _write_and_flush(fobj, data):
+ fobj.write(data)
+ fobj.flush()
+
+
+class _Forwarder(object):
+ """A file-like object that just forwards data written to it to a callable.
+ """
+
+ def __init__(self, callback):
+ self.callback = callback
+
+ def write(self, data):
+ self.callback(data)
+
+ def flush(self):
+ pass
+
+
+class Outputter(object):
+
+ def __init__(self, output_dir):
+ self.output_dir = output_dir
+ if output_dir:
+ self.output_txt = open(os.path.join(output_dir, 'output.txt'), 'w')
+ else:
+ self.output_txt = None
+
+ self.logfile_read = _Forwarder(self.serial_output)
+
+ self._log_handler = logging.StreamHandler(_Forwarder(self.log_output))
+ FORMAT = '<LAVA_DISPATCHER>%(asctime)s %(levelname)s: %(message)s'
+ DATEFMT = '%Y-%m-%d %I:%M:%S %p'
+ self._log_handler.setFormatter(
+ logging.Formatter(fmt=FORMAT, datefmt=DATEFMT))
+ del logging.root.handlers[:]
+ del logging.root.filters[:]
+ logging.root.addHandler(self._log_handler)
+
+ def serial_output(self, data):
+ _write_and_flush(sys.stdout, data)
+ if self.output_txt is not None:
+ _write_and_flush(self.output_txt, data)
+
+ # Currently all output is treated the same way.
+ log_output = serial_output
+
+ def write_named_data(self, name, data):
+ if self.output_dir is None:
+ return
+ with open(os.path.join(self.output_dir, name), 'w') as outf:
+ outf.write(data)
+
+
class LavaContext(object):
- def __init__(self, target, dispatcher_config, oob_file, job_data):
+ def __init__(self, target, dispatcher_config, oob_file, job_data, output_dir):
self.config = dispatcher_config
self.job_data = job_data
- # This is the file-like object to send serial output from the device
- # to. We just send it to stdout for now, but soon we'll do something
- # cleverer.
- self.logfile_read = sys.stdout
+ self.output = Outputter(output_dir)
+ self.logfile_read = self.output.logfile_read
device_config = get_device_config(
target, dispatcher_config.config_dir)
self._client = TargetBasedClient(self, device_config)
=== modified file 'lava_dispatcher/job.py'
@@ -111,11 +111,11 @@
class LavaTestJob(object):
- def __init__(self, job_json, oob_file, config):
+ def __init__(self, job_json, oob_file, config, output_dir):
self.job_status = 'pass'
self.load_job_data(job_json)
self.context = LavaContext(
- self.target, config, oob_file, self.job_data)
+ self.target, config, oob_file, self.job_data, output_dir)
def load_job_data(self, job_json):
self.job_data = json.loads(job_json)