Example #1
0
    def test_nonexistent_arg(self):
        """Makes sure that an exception is raised for unknown @ variables.

    A recipe writer needs to define a parsing tuple for each @ variable used by
    the recipe.
    """

        recipe_args = {
            'recipe_arg1': 'This should be replaced: @parameterone',
            'recipe_arg2': 'This arg cannot be replaced @nonexistent',
        }
        parser.set_defaults(**config.Config.get_extra())
        args = parser.parse_args([
            'value_for_param_one',
            '--optional_param',
            '3',
            '--spaces_param',
            'So unique!',
            'BOOM',
        ])

        with self.assertRaises(ValueError):
            imported = dftw_utils.import_args_from_dict(
                recipe_args, vars(args), config.Config)
            dftw_utils.check_placeholders(imported)
Example #2
0
    def test_import_args_from_cli(self):
        """Tries parsing the CLI arguments and updating a recipe dictionary."""

        recipe_args = {
            'recipe_arg1': 'This should remain intact',
            'recipe_arg2': 'This should be replaced: @parameterone',
            'recipe_arg3': 'This should be replaced by @optional_param',
            'recipe_arg4': 'This includes spaces: @spaces_param',
            'recipe_arg5': 'Characters after param: @explosion!',
        }

        expected_args = {
            'recipe_arg1': 'This should remain intact',
            'recipe_arg2': 'This should be replaced: value_for_param_one',
            'recipe_arg3': 'This should be replaced by 3',
            'recipe_arg4': 'This includes spaces: S P A C E',
            'recipe_arg5': 'Characters after param: BOOM!',
        }

        parser.set_defaults(**config.Config.get_extra())
        args = parser.parse_args([
            'value_for_param_one',
            '--optional_param',
            '3',
            '--spaces_param',
            'S P A C E',
            'BOOM',
        ])

        imported_args = dftw_utils.import_args_from_dict(
            recipe_args, vars(args), config.Config)

        self.assertEqual(imported_args, expected_args)
Example #3
0
 def test_config_fills_missing_args(self):
   """Tests that a configuration file will fill-in arguments that are missing
   from the CLI."""
   provided_args = {'arg1': 'This should remain intact', 'arg2': '@config'}
   expected_args = {
       'arg1': 'This should remain intact',
       'arg2': 'A config arg',
   }
   config.Config.load_extra_data('{"config": "A config arg"}')
   imported_args = dftw_utils.import_args_from_dict(
       provided_args, {}, config.Config)
   self.assertEqual(imported_args, expected_args)
Example #4
0
    def _setup_module_thread(module_description):
      """Calls the module's setup() function and sets an Event object for it.

      Args:
        module_description (dict): Corresponding recipe module description.
      """
      new_args = utils.import_args_from_dict(
          module_description['args'], vars(args), self.config)
      module = self._module_pool[module_description['name']]
      try:
        module.setup(**new_args)
      except Exception as error:  # pylint: disable=broad-except
        self.add_error(
            'An unknown error occurred: {0!s}'.format(error), critical=True)
      self.events[module_description['name']] = threading.Event()
Example #5
0
def main():
    """Main function for DFTimewolf."""
    parser = argparse.ArgumentParser(
        formatter_class=argparse.RawDescriptionHelpFormatter,
        description=generate_help())

    subparsers = parser.add_subparsers()

    for registered_recipe in config.Config.get_registered_recipes():
        recipe, recipe_args, documentation = registered_recipe
        subparser = subparsers.add_parser(
            recipe['name'],
            formatter_class=utils.DFTimewolfFormatterClass,
            description='{0:s}'.format(documentation))
        subparser.set_defaults(recipe=recipe)
        for switch, help_text, default in recipe_args:
            subparser.add_argument(switch, help=help_text, default=default)
        # Override recipe defaults with those specified in Config
        # so that they can in turn be overridden in the commandline
        subparser.set_defaults(**config.Config.get_extra())

    args = parser.parse_args()
    recipe = args.recipe

    # Thread all collectors.
    state = DFTimewolfState()

    for module_description in recipe['modules']:
        # Combine CLI args with args from the recipe description
        new_args = utils.import_args_from_dict(module_description['args'],
                                               vars(args), config.Config)

        # Create the module object and start processing
        module_name = module_description['name']
        print('Running module {0:s}'.format(module_name))
        module = config.Config.get_module(module_name)(state)
        module.setup(**new_args)
        state.check_errors()
        try:
            module.process()
        except DFTimewolfError as error:
            state.add_error(error.message, critical=True)

        # Check for eventual errors and clean up after each round.
        state.check_errors()
        state.cleanup()

    print('Recipe executed successfully.')
Example #6
0
  def test_cli_precedence_over_config(self):
    """Tests that the same argument provided via the CLI overrides the one
    specified in the config file."""

    provided_args = {
        'arg1': 'I want whatever CLI says: @parameterone',
    }
    expected_args = {
        'arg1': 'I want whatever CLI says: CLI WINS!',
    }

    config.Config.load_extra_data('{"parameterone": "CONFIG WINS!"}')
    args = parser.parse_args(['CLI WINS!', 'BOOM'])
    imported_args = dftw_utils.import_args_from_dict(
        provided_args, vars(args), config.Config)
    self.assertEqual(imported_args, expected_args)
Example #7
0
        def _setup_module_thread(module_description):
            """Calls the module's setup() function and sets an Event object for it.

      Args:
        module_description (dict): Corresponding recipe module description.
      """
            new_args = utils.import_args_from_dict(module_description['args'],
                                                   vars(args), self.config)
            module = self._module_pool[module_description['name']]
            try:
                module.setup(**new_args)
            except Exception as error:  # pylint: disable=broad-except
                self.add_error(
                    'An unknown error occurred: {0!s}\nFull traceback:\n{1:s}'.
                    format(error, traceback.format_exc()),
                    critical=True)

            self.events[module_description['name']] = threading.Event()
            self.cleanup()
Example #8
0
  def register_recipe(cls, recipe, **kwargs):
    """Registers a dftimewolf recipe.

    Registers a DFTimeWolf recipe with specified parameters. Parameters can be
    specified in three ways, in order of precedence:
      * Defined in config.json
      * Passed as arguments to the register_recipe function call
      * Passed as CLI args

    Args:
      recipe: imported python module representing the recipe.
      **kwargs: parameters to be replaced in the recipe before checking the
          CLI arguments.
    """
    # Update kwargs with what we already loaded from config.json
    kwargs.update(cls._extra_config)
    recipe.contents = dftw_utils.import_args_from_dict(recipe.contents, kwargs)
    recipe_name = recipe.contents['name']
    cls._recipe_classes[recipe_name] = (recipe.contents, recipe.args)
Example #9
0
def main():
    """Main function for DFTimewolf."""

    parser = argparse.ArgumentParser()

    subparsers = parser.add_subparsers(
        title=u'Available recipes',
        description=u'List of currently loaded recipes',
        help=u'Recipe-specific help')

    for recipe, recipe_args in config.Config.get_registered_recipes():
        subparser = subparsers.add_parser(recipe[u'name'],
                                          description=u'{0:s}'.format(
                                              recipe.__doc__))
        subparser.set_defaults(recipe=recipe)
        for switch, help_text in recipe_args:
            subparser.add_argument(switch, help=help_text)

    args = parser.parse_args()
    recipe = args.recipe

    console_out = dftw_utils.DFTimewolfConsoleOutput(sender=u'DFTimewolfCli',
                                                     verbose=True)

    # COLLECTORS
    # Thread collectors
    console_out.StdOut(u'Collectors:')
    for collector in recipe['collectors']:
        console_out.StdOut(u'  {0:s}'.format(collector[u'name']))

    collector_objs = []
    for collector in recipe[u'collectors']:
        new_args = dftw_utils.import_args_from_dict(collector[u'args'],
                                                    vars(args))
        collector_cls = config.Config.get_collector(collector[u'name'])
        collector_objs.extend(collector_cls.launch_collector(**new_args))

    # Wait for collectors to finish and collect output
    collector_output = []
    for collector_obj in collector_objs:
        collector_obj.join()
        collector_output.extend(collector_obj.results)

    if recipe[u'processors']:
        # PROCESSORS
        # Thread processors
        console_out.StdOut(u'Processors:')
        for processor in recipe[u'processors']:
            console_out.StdOut(u'  {0:s}'.format(processor[u'name']))

        processor_objs = []
        for processor in recipe[u'processors']:
            new_args = dftw_utils.import_args_from_dict(
                processor[u'args'], vars(args))
            new_args[u'collector_output'] = collector_output
            processor_class = config.Config.get_processor(processor[u'name'])
            processor_objs.extend(processor_class.launch_processor(**new_args))

        # Wait for processors to finish and collect output
        processor_output = []
        for processor in processor_objs:
            processor.join()
            processor_output.extend(processor.output)
    else:
        processor_output = collector_output

    # EXPORTERS
    # Thread exporters
    console_out.StdOut(u'Exporters:')
    for exporter in recipe[u'exporters']:
        console_out.StdOut(u'  {0:s}'.format(exporter[u'name']))

    exporter_objs = []
    for exporter in recipe[u'exporters']:
        new_args = dftw_utils.import_args_from_dict(exporter[u'args'],
                                                    vars(args))
        new_args[u'processor_output'] = processor_output
        exporter_class = config.Config.get_exporter(exporter[u'name'])
        exporter_objs.extend(exporter_class.launch_exporter(**new_args))

    # Wait for exporters to finish
    exporter_output = []
    for exporter in exporter_objs:
        exporter.join()
        exporter_output.extend(exporter.output)

    console_out.StdOut(u'Recipe {0:s} executed successfully'.format(
        recipe[u'name']))
Example #10
0
def main():
    """Main function for DFTimewolf."""
    parser = argparse.ArgumentParser()

    subparsers = parser.add_subparsers(
        title='Available recipes',
        description='List of currently loaded recipes',
        help=
        'Recipe-specific help. Run dftimewolf <RECIPE_NAME> -h for details.')

    for registered_recipe in config.Config.get_registered_recipes():
        recipe, recipe_args, documentation = registered_recipe
        subparser = subparsers.add_parser(
            recipe['name'], description='{0:s}'.format(documentation))
        subparser.set_defaults(recipe=recipe)
        for switch, help_text, default in recipe_args:
            subparser.add_argument(switch, help=help_text, default=default)

    args = parser.parse_args()
    recipe = args.recipe

    console_out = dftw_utils.DFTimewolfConsoleOutput(sender='DFTimewolfCli',
                                                     verbose=True)

    # Thread all collectors.
    console_out.StdOut('Collectors:')
    for collector in recipe['collectors']:
        console_out.StdOut('  {0:s}'.format(collector['name']))

    collector_objects = []
    for collector in recipe['collectors']:
        new_args = dftw_utils.import_args_from_dict(collector['args'],
                                                    vars(args), config.Config)
        collector_cls = config.Config.get_collector(collector['name'])
        collector_objects.extend(collector_cls.launch_collector(**new_args))

    # global_errors will contain any errors generated along the way by collectors,
    # producers or exporters.
    global_errors = []

    # Wait for collectors to finish and collect output.
    collector_output = []
    for collector_obj in collector_objects:
        collector_obj.join()
        collector_output.extend(collector_obj.results)
        if collector_obj.errors:
            #TODO(tomchop): Add name attributes in module objects
            error = (collector_obj.__class__.__name__,
                     ", ".join(collector_obj.errors))
            global_errors.append(error)
            console_out.StdErr("ERROR:{0:s}:{1:s}\n".format(*error))

    if recipe['processors']:
        # Thread processors.
        console_out.StdOut('Processors:')
        for processor in recipe['processors']:
            console_out.StdOut('  {0:s}'.format(processor['name']))

        processor_objs = []
        for processor in recipe['processors']:
            new_args = dftw_utils.import_args_from_dict(
                processor['args'], vars(args), config.Config)
            new_args['collector_output'] = collector_output
            processor_class = config.Config.get_processor(processor['name'])
            processor_objs.extend(processor_class.launch_processor(**new_args))

        # Wait for processors to finish and collect output
        processor_output = []
        for processor in processor_objs:
            processor.join()
            processor_output.extend(processor.output)
            if processor.errors:
                # Note: Should we fail if modules produce errors, or is warning the user
                # enough?
                # TODO(tomchop): Add name attributes in module objects.
                error = (processor.__class__.__name__,
                         ", ".join(processor.errors))
                global_errors.append(error)
                console_out.StdErr("ERROR:{0:s}:{1:s}\n".format(*error))

    else:
        processor_output = collector_output

    # Thread all exporters.
    if recipe['exporters']:
        console_out.StdOut('Exporters:')
        for exporter in recipe['exporters']:
            console_out.StdOut('  {0:s}'.format(exporter['name']))

        exporter_objs = []
        for exporter in recipe['exporters']:
            new_args = dftw_utils.import_args_from_dict(
                exporter['args'], vars(args), config.Config)
            new_args['processor_output'] = processor_output
            exporter_class = config.Config.get_exporter(exporter['name'])
            exporter_objs.extend(exporter_class.launch_exporter(**new_args))

        # Wait for exporters to finish.
        exporter_output = []
        for exporter in exporter_objs:
            exporter.join()
            exporter_output.extend(exporter.output)
            if exporter.errors:
                #TODO(tomchop): Add name attributes in module objects
                error = (exporter.__class__.__name__,
                         ", ".join(exporter.errors))
                global_errors.append(error)
                console_out.StdErr("ERROR:{0:s}:{1:s}\n".format(*error))
    else:
        exporter_output = processor_output

    if not global_errors:
        console_out.StdOut('Recipe {0:s} executed successfully'.format(
            recipe['name']))
    else:
        console_out.StdOut('Recipe {0:s} executed with {1:d} errors:'.format(
            recipe['name'], len(global_errors)))
        for error in global_errors:
            console_out.StdOut('  {0:s}: {1:s}'.format(*error))