Skip to content

compare plugin development

Jörg Stucke edited this page Oct 12, 2021 · 5 revisions

Compare Plug-in Development

There are many different aspects of Firmware that can be compared. Therefore, FACT provides a compare plug-in system, so that it is easy to extend the comparison.

Write a Compare Plug-in

FACT detects plug-ins automatically as long as they are stored in src/plugins/compare.

A plug-in consists of folders and files following the following template.

.
├── __init__.py
├── install.py [OPTIONAL]
├── apt-pkgs-runtime.txt [OPTIONAL]
├── apt-pkgs-build.txt [OPTIONAL]
├── dnf-pkgs-runtime.txt [OPTIONAL]
├── dnf-pkgs-build.txt [OPTIONAL]
├── code
│   ├── __init__.py
│   └── PLUGIN_NAME.py
├── internal [OPTIONAL]
│   └── ADDITIONAL_SOURCES_OR_CODE
├── test
│    ├── __init__.py
│    ├── test_PLUGIN_NAME.py
│    └── data [OPTIONAL]
│        └── SOME DATA FILES TO TEST
└── view [OPTIONAL]
    └── PLUGIN_NAME.html

Each file is described below.

./install.py

The same as for analysis plugins.

./code/PLUGIN_NAME.py

This is the actual plug-in code. You should use the following self-explanatory template to write your plug-in.

from compare.PluginBase import CompareBasePlugin


class ComparePlugin(CompareBasePlugin):
    '''
    Some Description
    '''

    NAME = 'PLUGIN_NAME'
    DEPENDENCIES = [LIST_ANALYSISES_THIS_PLUGIN_RELIES_ON]
    VERSION = 'x.x'

    def compare_function(self, fo_list):
        '''
        This function must be implemented by the plug-in.
        'fo_list' is a list with file_objects including analysis and all summaries
        this function should return a dictionary
        '''
        result = {}

        # your code...

        return result

The result dictionary should look like this:

{
    SECTION_ONE:
        {
            FIRST_FILE_OBJECT_ID: Value
            SECOND_FILE_OBJECT_ID: Value
            ...
            'collapse': True/False
        }
    SECTION_TWO:
        {
            'all': Value
            'collapse': True/False
        }
}

As you can see above there are two options of displaying stuff. In SECTION_ONE each File_Object has an individual value to display. In SECTION_TWO all File_Objects have a common value. If "Value" is a list, collapse specifies if this list is collapsed, when opening the analysis. If set to true, only an integer with the number of items in the list is shown in the web-interface. You can click this integer to display the whole list.

Your plug-in's configuration (if any) should be done in src/config/main.cfg in a section named as your plug-in. You can access it via "self.config".

🚫 Caution: Keys in the result dict must be strings. Otherwise, they cannot be stored in Mongo!
🚫 Caution: Value must not be sets. Otherwise, they cannot be stored in Mongo!

👍 Hint: Have a look at src.helperFunctions.compare_sets. You might find some useful methods simplifying the comparison of lists.

./internal

The optional folder internal can provide additional code or other stuff needed for the plug-in.
You can use the following code to get the absolute path of the internal directory:

import os
from common_helper_files import get_dir_of_file


INTERNAL_DIRECTORY_PATH = os.path.join(get_dir_of_file(__file__), '../internal')

If you want to load python libraries/files from the internal directory you can use the following code.

import os
import sys
from common_helper_files import get_dir_of_file


INTERNAL_DIRECTORY_PATH = os.path.join(get_dir_of_file(__file__), '../internal')

sys.path.append(INTERNAL_DIRECTORY_PATH)
from YOUR_PYTHON_FILE_IN_INTERNAL_DIRECTORY import WHATEVER

./test/test_PLUGIN_NAME.py

You should use the following template to test your plug-in:

from tests.compare_plugin_test_class import ComparePluginTest
 
from ..code.YOUR_PLUGIN import ComparePlugin
 
class TestComparePluginFileCoverage(ComparePluginTest):
     
    # This name must be changed according to the name of plug-in to test
    PLUGIN_NAME = 'YOUR_PLUGIN_NAME'
     
    def setup_plugin(self):
        """
        This function must be overwritten by the test instance.
        In most cases it is sufficient to copy this function.
        """
 
        # setup some config here if needed
        # self.config = ConfigParser()
 
        return ComparePlugin(self, config=self.config)
 
    def test_YOUR_FIRST_TEST(self):
        '''
        An initialized plugin instance is available at self.c_plugin
        '''
        YOUR TEST CODE

./test/data

This folder shall contain any additional files, that are needed for your tests. You can address this folder using the following code in your test code file.

import os
from common_helper_files import get_dir_of_file

TEST_DATA_DIR = os.path.join(get_dir_of_file(__file__), 'data')

./view/PLUGIN_NAME.html

This optional file can can contain a customized view utilizing Jinja2 template features. In general a standard template is used to show the results of an analysis. This standard view just generates a table from the result dictionary including all entries.

Clone this wiki locally