コード例 #1
0
def test_get_custom_fields_details():
    import_definition = package_helpers.get_import_definition_from_customize_py(
        mock_paths.MOCK_CUSTOMIZE_PY)
    import_def_data = sdk_helpers.get_from_export(
        import_definition,
        fields=["mock_field_number", "mock_field_text_area"])

    field_details = CmdDocgen._get_custom_fields_details(
        import_def_data.get("fields"))

    field_one = next(x for x in field_details
                     if x["api_name"] == "mock_field_number")
    field_two = next(x for x in field_details
                     if x["api_name"] == "mock_field_text_area")

    mock_field_one = {
        'api_name': u'mock_field_number',
        'label': u'Mock:  ล ฦ ว ศ ษ ส ห ฬ อ field number',
        'type': u'number',
        'prefix': u'properties',
        'placeholder': u'-',
        'tooltip': u'a mock tooltip  ล ฦ ว ศ ษ ส ห ฬ อ'
    }
    mock_field_two = {
        'api_name': u'mock_field_text_area',
        'label': u'Mock: Field Text Area  ล ฦ ว ศ ษ ส ห ฬ อ',
        'type': u'textarea',
        'prefix': u'properties',
        'placeholder': u'-',
        'tooltip': u'a tooltip  ล ฦ ว ศ ษ ส ห ฬ อ'
    }

    assert field_one == mock_field_one
    assert field_two == mock_field_two
コード例 #2
0
def test_get_datatable_details():
    import_definition = package_helpers.get_import_definition_from_customize_py(
        mock_paths.MOCK_CUSTOMIZE_PY)
    import_def_data = sdk_helpers.get_from_export(
        import_definition, datatables=["mock_data_table"])

    datatable_details = CmdDocgen._get_datatable_details(
        import_def_data.get("datatables"))
    the_datatable = datatable_details[0]

    mock_datatable = {
        'name':
        u'Mock: Data Table  ล ฦ ว ศ ษ ส ห ฬ อ',
        'anchor':
        u'mock-data-table--ล-ฦ-ว-ศ-ษ-ส-ห-ฬ-อ',
        'api_name':
        u'mock_data_table',
        'simple_name':
        u'mock-data-table----------',
        'columns': [{
            'name': u'mock col one',
            'api_name': u'mock_col_one',
            'type': u'text',
            'tooltip': u'a tooltip  ล ฦ ว ศ ษ ส ห ฬ อ'
        }, {
            'name': u'mock  ล ฦ ว ศ ษ ส ห ฬ อ col two',
            'api_name': u'mok_col_two',
            'type': u'number',
            'tooltip': u'tooltip  ล ฦ ว ศ ษ ส ห ฬ อ'
        }]
    }

    assert the_datatable == mock_datatable
コード例 #3
0
def test_get_message_destination_from_export(fx_mock_res_client):
    # TODO: Add test for all resilient objects...
    org_export = sdk_helpers.get_latest_org_export(fx_mock_res_client)

    export_data = sdk_helpers.get_from_export(org_export,
                                              message_destinations=["fn_main_mock_integration"])

    assert export_data.get("message_destinations")[0].get("name") == "fn_main_mock_integration"
コード例 #4
0
def test_get_object_api_names(fx_mock_res_client):
    org_export = sdk_helpers.get_latest_org_export(fx_mock_res_client)
    export_data = sdk_helpers.get_from_export(org_export,
                                              functions=["mock_function_one", "mock_function_two"])

    func_api_names = sdk_helpers.get_object_api_names("x_api_name", export_data.get("functions"))

    assert all(elem in ["mock_function_one", "mock_function_two"] for elem in func_api_names) is True
コード例 #5
0
def test_get_rule_details():
    import_definition = package_helpers.get_import_definition_from_customize_py(mock_paths.MOCK_CUSTOMIZE_PY)
    import_def_data = sdk_helpers.get_from_export(import_definition, rules=["Mock: Auto Rule"])

    rule_details = CmdDocgen._get_rule_details(import_def_data.get("rules"))
    the_rule = rule_details[0]

    mock_rule = {'name': u'Mock: Auto Rule', 'object_type': u'incident', 'workflow_triggered': u'mock_workflow_one', 'simple_name': u'mock-auto-rule'}

    assert the_rule == mock_rule
コード例 #6
0
def test_get_fn_input_details():
    import_definition = package_helpers.get_import_definition_from_customize_py(mock_paths.MOCK_CUSTOMIZE_PY)
    import_def_data = sdk_helpers.get_from_export(import_definition, functions=["mock_function_two"])

    fn = import_def_data.get("functions")[0]
    fn_inputs = CmdDocgen._get_fn_input_details(fn)

    fn_input = next(x for x in fn_inputs if x["api_name"] == "mock_input_number")
    mock_input = {'api_name': u'mock_input_number', 'name': u'mock_input_number', 'type': 'number', 'required': 'Yes', 'placeholder': u'-', 'tooltip': u'a mock tooltip  ล ฦ ว ศ ษ ส ห ฬ อ'}

    assert fn_input == mock_input
コード例 #7
0
def test_get_script_details():
    import_definition = package_helpers.get_import_definition_from_customize_py(mock_paths.MOCK_CUSTOMIZE_PY)
    import_def_data = sdk_helpers.get_from_export(import_definition, scripts=["Mock Script One"])
    scripts = import_def_data.get("scripts")
    script_details = CmdDocgen._get_script_details(scripts)
    the_script = script_details[0]

    assert the_script.get("name") == "Mock Script One"
    assert the_script.get("simple_name") == "mock-script-one"
    assert the_script.get("anchor") == "mock-script-one"
    assert the_script.get("description") == "a sample Artifact script"
    assert the_script.get("object_type") == "artifact"
    assert the_script.get("script_text") == """log.info("Print this message")"""
コード例 #8
0
def test_get_custom_artifact_details():
    import_definition = package_helpers.get_import_definition_from_customize_py(mock_paths.MOCK_CUSTOMIZE_PY)
    import_def_data = sdk_helpers.get_from_export(import_definition, artifact_types=["mock_artifact_2"])

    artifact_details = CmdDocgen._get_custom_artifact_details(import_def_data.get("artifact_types"))
    the_artifact = artifact_details[0]

    mock_artifact = {
        'api_name': u'mock_artifact_2',
        'display_name': u'Mock Artifact 2 ㌎ ㌏ ㌐ ㌑ ㌒ ㌓ ㌔ ㌕ ㌖',
        'description': u'㌎ ㌏ ㌐ ㌑ ㌒ ㌓ ㌔ ㌕ ㌖ ㌎ ㌏ ㌐ ㌑ ㌒ ㌓ ㌔ ㌕ ㌖asdf ㌎ ㌏ ㌐ ㌑ ㌒ ㌓ ㌔ ㌕ ㌖'
    }

    assert the_artifact == mock_artifact
コード例 #9
0
def test_get_function_details():
    import_definition = package_helpers.get_import_definition_from_customize_py(mock_paths.MOCK_CUSTOMIZE_PY)
    import_def_data = sdk_helpers.get_from_export(import_definition,
                                                  functions=["mock_function_two"],
                                                  workflows=["mock_workflow_two"])

    functions = import_def_data.get("functions")
    workflows = import_def_data.get("workflows")

    function_details = CmdDocgen._get_function_details(functions, workflows)
    the_function = function_details[0]

    assert the_function.get("name") == u"mock function  ล ฦ ว ศ ษ ส ห ฬ อ two"
    assert the_function.get("pre_processing_script") in u"""# mock pre script of function  ล ฦ ว ศ ษ ส ห ฬ อ ล ฦ ว ศ ษ ส ห ฬ อ ล ฦ ว ศ ษ ส ห ฬ อ two:\n\ninputs.mock_input_boolean = False\ninputs.mock_input_number = 1001\ninputs.mock_input_text = u" ล ฦ ว ศ ษ ส ห ฬ อ ล ฦ ว ศ ษ ส ห ฬ อ ramdom text" """
    assert the_function.get("post_processing_script") is None
コード例 #10
0
def test_get_obj_from_list(fx_mock_res_client):
    org_export = sdk_helpers.get_latest_org_export(fx_mock_res_client)
    export_data = sdk_helpers.get_from_export(org_export, functions=["mock_function_one", "mock_function_two"])

    all_functions = export_data.get("functions")
    got_functions = sdk_helpers.get_obj_from_list("export_key", all_functions)

    assert isinstance(got_functions, dict)
    assert "mock_function_one" in got_functions
    assert "mock_function_two" in got_functions

    # Test lambda condition
    got_functions = sdk_helpers.get_obj_from_list("export_key", all_functions, lambda o: True if o.get("export_key") == "mock_function_one" else False)
    assert "mock_function_one" in got_functions
    assert "mock_function_two" not in got_functions
コード例 #11
0
def test_get_related_objects_when_getting_from_export(fx_mock_res_client, get_related_param):

    org_export = sdk_helpers.get_latest_org_export(fx_mock_res_client)

    export_data = sdk_helpers.get_from_export(org_export,
                                              message_destinations=["fn_main_mock_integration"],
                                              functions=["mock_function__three"],
                                              get_related_objects=get_related_param)

    assert export_data.get("message_destinations")[0].get("name") == "fn_main_mock_integration"

    if get_related_param:
        assert len(export_data.get("functions", [])) > 0
        assert any(elem.get("name") in ("mock_function_one", "mock_function_two") for elem in export_data.get("functions")) is True

    else:
        assert all(elem.get("name") in ("mock_function_one", "mock_function_two") for elem in export_data.get("functions")) is False
コード例 #12
0
def test_reload_package_w_playbook(
        fx_copy_fn_main_mock_integration_w_playbooks, fx_get_sub_parser,
        fx_cmd_line_args_codegen_reload):

    output_path = os.path.join(mock_paths.TEST_TEMP_DIR, "mock_path",
                               "fn_main_mock_integration-1.1.0")
    mock_integration_name = fx_copy_fn_main_mock_integration_w_playbooks[0]
    shutil.move(fx_copy_fn_main_mock_integration_w_playbooks[1], output_path)

    # Replace cmd line arg "fn_main_mock_integration" with path to temp dir location
    sys.argv[sys.argv.index(mock_integration_name)] = output_path

    # Add path to a mock export.res file
    sys.argv.extend(["-e", mock_paths.MOCK_RELOAD_EXPORT_RES_W_PLAYBOOK])

    cmd_codegen = CmdCodegen(fx_get_sub_parser)
    args = cmd_codegen.parser.parse_known_args()[0]
    path_package_reloaded = cmd_codegen._reload_package(args)

    import_definition = package_helpers.get_import_definition_from_customize_py(
        os.path.join(path_package_reloaded, mock_integration_name,
                     package_helpers.PATH_CUSTOMIZE_PY))

    res_objs = sdk_helpers.get_from_export(
        import_definition,
        rules=["Additional Mock Rule", "Mock Manual Rule"],
        playbooks=["main_mock_playbook"])

    general_test_package_structure(mock_integration_name,
                                   path_package_reloaded)

    assert helpers.verify_expected_list(
        ["Additional Mock Rule", "Mock Manual Rule"],
        [o.get("x_api_name") for o in res_objs.get("rules")])
    assert helpers.verify_expected_list(
        ["main_mock_playbook"],
        [o.get("x_api_name") for o in res_objs.get("playbooks")])
コード例 #13
0
    def _gen_package(args, setup_py_attributes={}):

        LOG.info("Generating codegen package...")

        if not sdk_helpers.is_valid_package_name(args.package):
            raise SDKException(u"'{0}' is not a valid package name".format(args.package))

        # Strip off version information, if present in package base folder, to get the package name.
        package_name = re.split(VERSION_REGEX, args.package, 1)[0]
        # Get base version if we are running against a package base folder with version.
        base_version = ''.join(re.split(package_name, args.package))

        # Get output_base, use args.output if defined, else current directory
        output_base = args.output if args.output else os.curdir
        output_base = os.path.abspath(output_base)

        # If --exportfile is specified, read org_export from that file
        if args.exportfile:
            LOG.info("Using local export file: %s", args.exportfile)
            org_export = sdk_helpers.read_local_exportfile(args.exportfile)

        else:
            # Instantiate connection to the Resilient Appliance
            res_client = sdk_helpers.get_resilient_client()

            # Generate + get latest export from Resilient Server
            org_export = sdk_helpers.get_latest_org_export(res_client)

        # Get data required for Jinja2 templates from export
        jinja_data = sdk_helpers.get_from_export(org_export,
                                                 message_destinations=args.messagedestination,
                                                 functions=args.function,
                                                 workflows=args.workflow,
                                                 rules=args.rule,
                                                 fields=args.field,
                                                 artifact_types=args.artifacttype,
                                                 datatables=args.datatable,
                                                 tasks=args.task,
                                                 scripts=args.script)

        # Get 'minified' version of the export. This is used in customize.py
        jinja_data["export_data"] = sdk_helpers.minify_export(org_export,
                                                              message_destinations=sdk_helpers.get_object_api_names(ResilientObjMap.MESSAGE_DESTINATIONS, jinja_data.get("message_destinations")),
                                                              functions=sdk_helpers.get_object_api_names(ResilientObjMap.FUNCTIONS, jinja_data.get("functions")),
                                                              workflows=sdk_helpers.get_object_api_names(ResilientObjMap.WORKFLOWS, jinja_data.get("workflows")),
                                                              rules=sdk_helpers.get_object_api_names(ResilientObjMap.RULES, jinja_data.get("rules")),
                                                              fields=jinja_data.get("all_fields"),
                                                              artifact_types=sdk_helpers.get_object_api_names(ResilientObjMap.INCIDENT_ARTIFACT_TYPES, jinja_data.get("artifact_types")),
                                                              datatables=sdk_helpers.get_object_api_names(ResilientObjMap.DATATABLES, jinja_data.get("datatables")),
                                                              tasks=sdk_helpers.get_object_api_names(ResilientObjMap.TASKS, jinja_data.get("tasks")),
                                                              phases=sdk_helpers.get_object_api_names(ResilientObjMap.PHASES, jinja_data.get("phases")),
                                                              scripts=sdk_helpers.get_object_api_names(ResilientObjMap.SCRIPTS, jinja_data.get("scripts")))

        # Add package_name to jinja_data
        jinja_data["package_name"] = package_name

        # Add version
        jinja_data["version"] = setup_py_attributes.get("version", package_helpers.MIN_SETUP_PY_VERSION)

        # Validate we have write permissions
        sdk_helpers.validate_dir_paths(os.W_OK, output_base)

        # Join package_name to output base (add base version if running against a folder which includes a version).
        output_base = os.path.join(output_base, package_name+base_version)

        # If the output_base directory does not exist, create it
        if not os.path.exists(output_base):
            os.makedirs(output_base)

        # Instansiate Jinja2 Environment with path to Jinja2 templates
        jinja_env = sdk_helpers.setup_jinja_env("data/codegen/templates/package_template")

        # This dict maps our package file structure to  Jinja2 templates
        package_mapping_dict = {
            "MANIFEST.in": ("MANIFEST.in.jinja2", jinja_data),
            "README.md": ("README.md.jinja2", jinja_data),
            "setup.py": ("setup.py.jinja2", jinja_data),
            "tox.ini": ("tox.ini.jinja2", jinja_data),
            "Dockerfile": ("Dockerfile.jinja2", jinja_data),
            "entrypoint.sh": ("entrypoint.sh.jinja2", jinja_data),
            "apikey_permissions.txt": ("apikey_permissions.txt.jinja2", jinja_data),
            "data": {},
            "icons": {
                "company_logo.png": package_helpers.PATH_DEFAULT_ICON_COMPANY_LOGO,
                "app_logo.png": package_helpers.PATH_DEFAULT_ICON_EXTENSION_LOGO,
            },
            "doc": {
                "screenshots": {
                    "main.png": package_helpers.PATH_DEFAULT_SCREENSHOT
                }
            },
            package_name: {
                "__init__.py": ("package/__init__.py.jinja2", jinja_data),
                "LICENSE": ("package/LICENSE.jinja2", jinja_data),

                "components": {
                    "__init__.py": ("package/components/__init__.py.jinja2", jinja_data),
                },
                "util": {
                    "data": {
                        "export.res": ("package/util/data/export.res.jinja2", jinja_data)
                    },
                    "__init__.py": ("package/util/__init__.py.jinja2", jinja_data),
                    "config.py": ("package/util/config.py.jinja2", jinja_data),
                    "customize.py": ("package/util/customize.py.jinja2", jinja_data),
                    "selftest.py": ("package/util/selftest.py.jinja2", jinja_data),
                }
            }
        }

        # If there are Functions, add a 'tests' directory
        if jinja_data.get("functions"):
            package_mapping_dict["tests"] = {}

        # Loop each Function
        for f in jinja_data.get("functions"):
            # Add package_name to function data
            f["package_name"] = package_name

            # Generate function_component.py file name
            file_name = u"funct_{0}.py".format(f.get("export_key"))

            # Add to 'components' directory
            package_mapping_dict[package_name]["components"][file_name] = ("package/components/function.py.jinja2", f)

            # Add to 'tests' directory
            package_mapping_dict["tests"][u"test_{0}".format(file_name)] = ("tests/test_function.py.jinja2", f)

        for w in jinja_data.get("workflows"):

            # Generate wf_xx.md file name
            file_name = u"wf_{0}.md".format(w.get(ResilientObjMap.WORKFLOWS))

            # Add workflow to data directory
            package_mapping_dict["data"][file_name] = ("data/workflow.md.jinja2", w)

        newly_generated_files, skipped_files = CmdCodegen.render_jinja_mapping(
            jinja_mapping_dict=package_mapping_dict,
            jinja_env=jinja_env,
            target_dir=output_base,
            package_dir=output_base)

        # Log new and skipped files
        if newly_generated_files:
            LOG.debug("Newly generated files:\n\t> %s", "\n\t> ".join(newly_generated_files))

        if skipped_files:
            LOG.debug("Files Skipped:\n\t> %s", "\n\t> ".join(skipped_files))

        LOG.info("'codegen' complete for '%s'", package_name)
コード例 #14
0
    def _set_version(args):

        new_version = args.set_version

        if not sdk_helpers.is_valid_version_syntax(new_version):
            raise SDKException(
                "{0} is not a valid version".format(new_version))

        new_version_int = list(map(int, (re.findall(r"\d+", new_version))))

        # Get absolute path_to_src
        path_to_src = os.path.abspath(args.package)

        # Get path to setup.py file
        path_setup_py_file = os.path.join(path_to_src,
                                          package_helpers.BASE_NAME_SETUP_PY)

        # Parse the setup.py file
        setup_py_attributes = package_helpers.parse_setup_py(
            path_setup_py_file,
            package_helpers.SUPPORTED_SETUP_PY_ATTRIBUTE_NAMES)

        package_name = setup_py_attributes.get("name", "")

        LOG.info("Setting Resilient Platform version for %s to %s",
                 package_name, new_version)

        # Get the customize file location.
        path_customize_py = package_helpers.get_configuration_py_file_path(
            "customize", setup_py_attributes)

        # Get customize.py ImportDefinition
        customize_py_import_definition = package_helpers.get_import_definition_from_customize_py(
            path_customize_py)

        old_version = customize_py_import_definition["server_version"][
            "version"]

        LOG.info("Old Version: %s", old_version)
        LOG.info("New Version: %s", new_version)

        # Set the new version
        customize_py_import_definition["server_version"][
            "version"] = new_version
        customize_py_import_definition["server_version"][
            "major"] = new_version_int[0]
        customize_py_import_definition["server_version"][
            "minor"] = new_version_int[1]
        customize_py_import_definition["server_version"][
            "build_number"] = new_version_int[2]

        LOG.info("Loading old customize.py file")

        # Load the customize.py module
        customize_py_module = package_helpers.load_customize_py_module(
            path_customize_py, warn=False)

        # Get the 'old_params' from customize.py
        old_params = customize_py_module.codegen_reload_data()

        # Rename the old customize.py with .bak
        path_customize_py_bak = sdk_helpers.rename_to_bak_file(
            path_customize_py)

        # If local export file exists then save it to a .bak file.
        # (Older packages may not have the /util/data/export.res file)
        # Figure out the path of the files first
        dir_customize_py = os.path.dirname(path_customize_py)
        path_local_export_res = os.path.join(
            dir_customize_py, package_helpers.PATH_LOCAL_EXPORT_RES)

        path_local_export_res_bak = None
        if os.path.isfile(path_local_export_res):
            path_local_export_res_bak = sdk_helpers.rename_to_bak_file(
                path_local_export_res)

        try:

            jinja_data = sdk_helpers.get_from_export(
                customize_py_import_definition,
                message_destinations=old_params.get("message_destinations"),
                functions=old_params.get("functions"),
                workflows=old_params.get("workflows"),
                rules=old_params.get("actions"),
                fields=old_params.get("incident_fields"),
                artifact_types=old_params.get("incident_artifact_types"),
                datatables=old_params.get("datatables"),
                tasks=old_params.get("automatic_tasks"),
                scripts=old_params.get("scripts"))

            jinja_data["export_data"] = sdk_helpers.minify_export(
                customize_py_import_definition,
                message_destinations=sdk_helpers.get_object_api_names(
                    ResilientObjMap.MESSAGE_DESTINATIONS,
                    jinja_data.get("message_destinations")),
                functions=sdk_helpers.get_object_api_names(
                    ResilientObjMap.FUNCTIONS, jinja_data.get("functions")),
                workflows=sdk_helpers.get_object_api_names(
                    ResilientObjMap.WORKFLOWS, jinja_data.get("workflows")),
                rules=sdk_helpers.get_object_api_names(
                    ResilientObjMap.RULES, jinja_data.get("rules")),
                fields=jinja_data.get("all_fields"),
                artifact_types=sdk_helpers.get_object_api_names(
                    ResilientObjMap.INCIDENT_ARTIFACT_TYPES,
                    jinja_data.get("artifact_types")),
                datatables=sdk_helpers.get_object_api_names(
                    ResilientObjMap.DATATABLES, jinja_data.get("datatables")),
                tasks=sdk_helpers.get_object_api_names(
                    ResilientObjMap.TASKS, jinja_data.get("tasks")),
                phases=sdk_helpers.get_object_api_names(
                    ResilientObjMap.PHASES, jinja_data.get("phases")),
                scripts=sdk_helpers.get_object_api_names(
                    ResilientObjMap.SCRIPTS, jinja_data.get("scripts")))

            # Add package_name to jinja_data
            jinja_data["package_name"] = package_name

            # Add version
            jinja_data["version"] = setup_py_attributes.get(
                "version", package_helpers.MIN_SETUP_PY_VERSION)

            # Instansiate Jinja2 Environment with path to Jinja2 templates for customize.py
            jinja_env = sdk_helpers.setup_jinja_env(
                "data/codegen/templates/package_template/package/util")
            jinja_template = jinja_env.get_template("customize.py.jinja2")

            LOG.info("Writing new customize.py file")

            # Render & write jinja2 template
            jinja_rendered_text = jinja_template.render(jinja_data)
            sdk_helpers.write_file(path_customize_py, jinja_rendered_text)

            # Instansiate Jinja2 Environment with path to Jinja2 templates for /util/dat/export.res
            #jinja_env = sdk_helpers.setup_jinja_env("data/codegen/templates/package_template/package/util/data")
            jinja_template = jinja_env.get_template("/data/export.res.jinja2")

            LOG.debug("Writing new /util/data/export.res file")

            # Render jinja2 template
            jinja_rendered_text = jinja_template.render(jinja_data)

            # Make sure the /util/data directory is there if it is not
            dir_local_export_res = os.path.dirname(path_local_export_res)
            if not os.path.exists(dir_local_export_res):
                os.makedirs(dir_local_export_res)

            # Write the file
            sdk_helpers.write_file(path_local_export_res, jinja_rendered_text)

            LOG.info("'dev --set-version' complete for '%s'", package_name)

        except Exception as err:
            LOG.error(
                u"Error running resilient-sdk dev --set-version\n\nERROR:%s",
                err)

        # This is required in finally block as user may kill using keyboard interrupt
        finally:
            # If an error occurred, customize.py does not exist, rename the backup file to original
            if not os.path.isfile(path_customize_py):
                LOG.info(
                    u"An error occurred. Renaming customize.py.bak to customize.py"
                )
                sdk_helpers.rename_file(path_customize_py_bak,
                                        package_helpers.BASE_NAME_CUSTOMIZE_PY)
            if path_local_export_res_bak and not os.path.isfile(
                    path_local_export_res):
                LOG.info(
                    u"An error occurred. Renaming /util/data/export.res.bak to export.res"
                )
                sdk_helpers.rename_file(
                    path_local_export_res_bak,
                    package_helpers.BASE_NAME_LOCAL_EXPORT_RES)
コード例 #15
0
def test_reload_package(fx_copy_fn_main_mock_integration, fx_get_sub_parser,
                        fx_cmd_line_args_codegen_reload):
    """
    This tests that when a package is reloaded with codegen --reload
    that each of the EXPECTED_FILES exist and also the additional 'Additional Mock Rule'
    and its related Workflow which has a Function is also added to the package
    """

    output_path = os.path.join(mock_paths.TEST_TEMP_DIR, "mock_path",
                               "fn_main_mock_integration-1.1.0")
    mock_integration_name = fx_copy_fn_main_mock_integration[0]
    shutil.move(fx_copy_fn_main_mock_integration[1], output_path)

    # Replace cmd line arg "fn_main_mock_integration" with path to temp dir location
    sys.argv[sys.argv.index(mock_integration_name)] = output_path

    # Add path to a mock export.res file
    sys.argv.extend(["-e", mock_paths.MOCK_RELOAD_EXPORT_RES])

    cmd_codegen = CmdCodegen(fx_get_sub_parser)
    args = cmd_codegen.parser.parse_known_args()[0]
    path_package_reloaded = cmd_codegen._reload_package(args)

    # This is really getting the import definition from the new data/export.res file, so tests that as well
    import_definition = package_helpers.get_import_definition_from_customize_py(
        os.path.join(path_package_reloaded, mock_integration_name,
                     package_helpers.PATH_CUSTOMIZE_PY))

    res_objs = sdk_helpers.get_from_export(
        import_definition,
        rules=["Additional Mock Rule", "Mock Manual Rule"],
        functions=[
            "additional_mock_function", "mock_function_one",
            "funct_new_mock_function", "func_new_mock_function"
        ],
        workflows=[
            "additional_mock_workflow", "mock_workflow_one",
            "wf_new_mock_workflow"
        ])

    # Assert the general structure of the reloaded package
    general_test_package_structure(mock_integration_name,
                                   path_package_reloaded)

    # Assert the additional rule, function and workflow were added
    assert helpers.verify_expected_list(
        ["Additional Mock Rule", "Mock Manual Rule"],
        [o.get("x_api_name") for o in res_objs.get("rules")])
    assert helpers.verify_expected_list([
        "additional_mock_function", "mock_function_one",
        "funct_new_mock_function", "func_new_mock_function"
    ], [o.get("x_api_name") for o in res_objs.get("functions")])
    assert helpers.verify_expected_list([
        "additional_mock_workflow", "mock_workflow_one", "wf_new_mock_workflow"
    ], [o.get("x_api_name") for o in res_objs.get("workflows")])

    # Assert a new components file is created
    expected_component_files = EXPECTED_FILES_PACKAGE_COMPONENTS_DIR + [
        "funct_additional_mock_function.py"
    ]
    assert helpers.verify_expected_list(
        expected_component_files,
        os.listdir(
            os.path.join(path_package_reloaded, mock_integration_name,
                         "components")))

    # Assert a new components file with prefix 'funct_' is created
    expected_component_files = ["funct_new_mock_function.py"]
    assert helpers.verify_expected_list(
        expected_component_files,
        os.listdir(
            os.path.join(path_package_reloaded, mock_integration_name,
                         "components")))

    # Assert a new components file with prefix 'func_' is created
    expected_component_files = ["func_new_mock_function.py"]
    assert helpers.verify_expected_list(
        expected_component_files,
        os.listdir(
            os.path.join(path_package_reloaded, mock_integration_name,
                         "components")))

    # Assert a new tests file is created
    expected_test_files = EXPECTED_FILES_TESTS_DIR + [
        "test_funct_additional_mock_function.py"
    ]
    assert helpers.verify_expected_list(
        expected_test_files,
        os.listdir(os.path.join(path_package_reloaded, "tests")))

    # Assert a new tests file including 'func_' is created.
    expected_test_files = ["test_func_new_mock_function.py"]
    assert helpers.verify_expected_list(
        expected_test_files,
        os.listdir(os.path.join(path_package_reloaded, "tests")))

    # Assert a new md file is created in data dir
    expected_workflow_files = EXPECTED_FILES_DATA_DIR + [
        "wf_additional_mock_workflow.md"
    ]
    assert helpers.verify_expected_list(
        expected_workflow_files,
        os.listdir(os.path.join(path_package_reloaded, "data")))

    # Assert a new md file with 'wf_' is created in data dir
    expected_workflow_files = ["wf_new_mock_workflow.md"]
    assert helpers.verify_expected_list(
        expected_workflow_files,
        os.listdir(os.path.join(path_package_reloaded, "data")))

    # Remove files from generated package path and recreate without prefix or substring of 'funct_' or 'wf_'.
    os.remove(
        os.path.join(path_package_reloaded, mock_integration_name,
                     "components", "funct_additional_mock_function.py"))
    Path(
        os.path.join(path_package_reloaded, mock_integration_name,
                     "components", "additional_mock_function.py")).touch()
    os.remove(
        os.path.join(path_package_reloaded, "tests",
                     "test_funct_additional_mock_function.py"))
    Path(
        os.path.join(path_package_reloaded, "tests",
                     "test_additional_mock_function.py")).touch()
    os.remove(
        os.path.join(path_package_reloaded, "data",
                     "wf_additional_mock_workflow.md"))
    Path(
        os.path.join(path_package_reloaded, "data",
                     "additional_mock_workflow.md")).touch()

    # Get modification time for workflow file "wf_mock_workflow_one.md" in seconds since the epoch.'
    wf_modified_time = os.path.getmtime(
        os.path.join(path_package_reloaded, "data", "wf_mock_workflow_one.md"))

    # Perform another test reload.
    cmd_codegen = CmdCodegen(fx_get_sub_parser)
    args = cmd_codegen.parser.parse_known_args()[0]
    path_package_reloaded = cmd_codegen._reload_package(args)

    # This is really getting the import definition from the new data/export.res file, so tests that as well
    import_definition = package_helpers.get_import_definition_from_customize_py(
        os.path.join(path_package_reloaded, mock_integration_name,
                     package_helpers.PATH_CUSTOMIZE_PY))

    res_objs = sdk_helpers.get_from_export(
        import_definition,
        rules=["Additional Mock Rule", "Mock Manual Rule"],
        functions=[
            "additional_mock_function", "mock_function_one",
            "funct_new_mock_function", "func_new_mock_function"
        ],
        workflows=[
            "additional_mock_workflow", "mock_workflow_one",
            "wf_new_mock_workflow"
        ])

    # Assert the general structure of the reloaded package
    general_test_package_structure(mock_integration_name,
                                   path_package_reloaded)

    # Assert a new components file with 'funct_'  prefix is not created
    expected_component_files = ["funct_additional_mock_function.py"]
    assert not helpers.verify_expected_list(
        expected_component_files,
        os.listdir(
            os.path.join(path_package_reloaded, mock_integration_name,
                         "components")))
    # Assert a new workflow file with 'md_' prefix is not created in data dir
    expected_workflow_files = ["wf_additional_mock_workflow.md"]
    assert not helpers.verify_expected_list(
        expected_workflow_files,
        os.listdir(os.path.join(path_package_reloaded, "data")))
    # Assert a new tests file with "funct_" substring is not created
    expected_test_files = ["test_func_additional_mock_function.py"]
    assert not helpers.verify_expected_list(
        expected_test_files,
        os.listdir(os.path.join(path_package_reloaded, "tests")))
    # Get new modification time for test workflow file.
    new_wf_modified_time = os.path.getmtime(
        os.path.join(path_package_reloaded, "data", "wf_mock_workflow_one.md"))
    # Assert modification time of workflow has been updated.
    assert new_wf_modified_time > wf_modified_time
コード例 #16
0
    def _gen_package(args, setup_py_attributes={}):

        LOG.info("Generating codegen package...")

        sdk_helpers.is_python_min_supported_version()

        if os.path.exists(args.package) and not args.reload:
            raise SDKException(u"'{0}' already exists. Add --reload flag to regenerate it".format(args.package))

        if not sdk_helpers.is_valid_package_name(args.package):
            raise SDKException(u"'{0}' is not a valid package name".format(args.package))

        # The package_name will be specified in the args
        package_name = args.package

        # Get output_base, use args.output if defined, else current directory
        output_base = args.output if args.output else os.curdir
        output_base = os.path.abspath(output_base)

        # If --exportfile is specified, read org_export from that file
        if args.exportfile:
            LOG.info("Using local export file: %s", args.exportfile)
            org_export = sdk_helpers.read_local_exportfile(args.exportfile)

        else:
            # Instantiate connection to the Resilient Appliance
            res_client = sdk_helpers.get_resilient_client(path_config_file=args.config)

            # Generate + get latest export from Resilient Server
            org_export = sdk_helpers.get_latest_org_export(res_client)

        # Get data required for Jinja2 templates from export
        jinja_data = sdk_helpers.get_from_export(org_export,
                                                 message_destinations=args.messagedestination,
                                                 functions=args.function,
                                                 workflows=args.workflow,
                                                 rules=args.rule,
                                                 fields=args.field,
                                                 artifact_types=args.artifacttype,
                                                 datatables=args.datatable,
                                                 tasks=args.task,
                                                 scripts=args.script,
                                                 incident_types=args.incidenttype,
                                                 playbooks=args.playbook)

        # Get 'minified' version of the export. This is used in customize.py
        jinja_data["export_data"] = sdk_helpers.minify_export(org_export,
                                                              message_destinations=sdk_helpers.get_object_api_names(ResilientObjMap.MESSAGE_DESTINATIONS, jinja_data.get("message_destinations")),
                                                              functions=sdk_helpers.get_object_api_names(ResilientObjMap.FUNCTIONS, jinja_data.get("functions")),
                                                              workflows=sdk_helpers.get_object_api_names(ResilientObjMap.WORKFLOWS, jinja_data.get("workflows")),
                                                              rules=sdk_helpers.get_object_api_names(ResilientObjMap.RULES, jinja_data.get("rules")),
                                                              fields=jinja_data.get("all_fields"),
                                                              artifact_types=sdk_helpers.get_object_api_names(ResilientObjMap.INCIDENT_ARTIFACT_TYPES, jinja_data.get("artifact_types")),
                                                              datatables=sdk_helpers.get_object_api_names(ResilientObjMap.DATATABLES, jinja_data.get("datatables")),
                                                              tasks=sdk_helpers.get_object_api_names(ResilientObjMap.TASKS, jinja_data.get("tasks")),
                                                              phases=sdk_helpers.get_object_api_names(ResilientObjMap.PHASES, jinja_data.get("phases")),
                                                              scripts=sdk_helpers.get_object_api_names(ResilientObjMap.SCRIPTS, jinja_data.get("scripts")),
                                                              incident_types=sdk_helpers.get_object_api_names(ResilientObjMap.INCIDENT_TYPES, jinja_data.get("incident_types")),
                                                              playbooks=sdk_helpers.get_object_api_names(ResilientObjMap.PLAYBOOKS, jinja_data.get("playbooks")))

        # Add package_name to jinja_data
        jinja_data["package_name"] = package_name

        # Add version
        jinja_data["version"] = setup_py_attributes.get("version", package_helpers.MIN_SETUP_PY_VERSION)

        jinja_data["resilient_libraries_version"] = sdk_helpers.get_resilient_libraries_version_to_use()

        # Validate we have write permissions
        sdk_helpers.validate_dir_paths(os.W_OK, output_base)

        if not args.reload:
            # If this is not a reload, join package_name to output base
            output_base = os.path.join(output_base, package_name)

        # If the output_base directory does not exist, create it
        if not os.path.exists(output_base):
            os.makedirs(output_base)

        # Instansiate Jinja2 Environment with path to Jinja2 templates
        jinja_env = sdk_helpers.setup_jinja_env(constants.PACKAGE_TEMPLATE_PATH)

        # This dict maps our package file structure to  Jinja2 templates
        package_mapping_dict = {
            "MANIFEST.in": ("MANIFEST.in.jinja2", jinja_data),
            "README.md": ("README.md.jinja2", jinja_data),
            "setup.py": ("setup.py.jinja2", jinja_data),
            "tox.ini": ("tox.ini.jinja2", jinja_data),
            "Dockerfile": ("Dockerfile.jinja2", jinja_data),
            "entrypoint.sh": ("entrypoint.sh.jinja2", jinja_data),
            "apikey_permissions.txt": ("apikey_permissions.txt.jinja2", jinja_data),
            "data": {},
            "icons": {
                "company_logo.png": package_helpers.PATH_DEFAULT_ICON_COMPANY_LOGO,
                "app_logo.png": package_helpers.PATH_DEFAULT_ICON_EXTENSION_LOGO,
            },
            "doc": {
                "screenshots": {
                    "main.png": package_helpers.PATH_DEFAULT_SCREENSHOT
                }
            },
            package_name: {
                "__init__.py": ("package/__init__.py.jinja2", jinja_data),
                "LICENSE": ("package/LICENSE.jinja2", jinja_data),

                "components": {
                    "__init__.py": ("package/components/__init__.py.jinja2", jinja_data),
                },
                "util": {
                    "data": {
                        "export.res": ("package/util/data/export.res.jinja2", jinja_data)
                    },
                    "__init__.py": ("package/util/__init__.py.jinja2", jinja_data),
                    "config.py": ("package/util/config.py.jinja2", jinja_data),
                    "customize.py": ("package/util/customize.py.jinja2", jinja_data),
                    "selftest.py": ("package/util/selftest.py.jinja2", jinja_data),
                }
            }
        }

        # If there are Functions, add a 'tests' and a 'payload_samples' directory (if in dev mode)
        if jinja_data.get("functions"):
            package_mapping_dict["tests"] = {}
            package_mapping_dict[package_helpers.BASE_NAME_PAYLOAD_SAMPLES_DIR] = {}

        # Get a list of function names in export.
        fn_names = [f.get(ResilientObjMap.FUNCTIONS) for f in jinja_data.get("functions")]

        # Loop each Function
        for f in jinja_data.get("functions"):
            # Add package_name to function data
            f["package_name"] = package_name

            # Get function name
            fn_name = f.get(ResilientObjMap.FUNCTIONS)

            # Generate funct_function_component.py file name
            # Don't add prefix if function name already begins with "func_" or "funct_".
            if re.search(r"^(func|funct)_", fn_name):
                file_name = u"{0}.py".format(fn_name)
            else:
                file_name = u"funct_{0}.py".format(fn_name)
                # Check if file_name without extension already exists in functions names list.
                if os.path.splitext(file_name)[0] in fn_names:
                    raise SDKException(u"File name '{0}' already in use please rename the function '{1}'."
                                       .format(file_name, fn_name))

            # Add an 'atomic function' to 'components' directory else add a 'normal function'
            package_mapping_dict[package_name]["components"][file_name] = ("package/components/atomic_function.py.jinja2", f)

            # Add to 'tests' directory
            package_mapping_dict["tests"][u"test_{0}".format(file_name)] = ("tests/test_function.py.jinja2", f)

            # Add a 'payload_samples/fn_name' directory and the files to it
            CmdCodegen.add_payload_samples(package_mapping_dict, fn_name, f)

        # Get a list of workflow names in export.
        wf_names = [w.get(ResilientObjMap.WORKFLOWS) for w in jinja_data.get("workflows")]

        for w in jinja_data.get("workflows"):
            # Get workflow name
            wf_name = w.get(ResilientObjMap.WORKFLOWS)

            # Generate wf_xx.md file name
            # Don't add prefix if workflow name already begins with "wf_".
            if re.search(r"^wf_", wf_name):
                file_name = u"{0}.md".format(wf_name)
            else:
                file_name = u"wf_{0}.md".format(wf_name)
                # Check if file_name without extension already exists in workflow names list.
                if os.path.splitext(file_name)[0] in wf_names:
                    raise SDKException(u"File name '{0}' already in use please recreate the workflow '{1}'."
                                       .format(file_name, wf_name))

            # Add workflow to data directory
            package_mapping_dict["data"][file_name] = ("data/workflow.md.jinja2", w)

        newly_generated_files, skipped_files = CmdCodegen.render_jinja_mapping(
            jinja_mapping_dict=package_mapping_dict,
            jinja_env=jinja_env,
            target_dir=output_base,
            package_dir=output_base)

        # Log new and skipped files
        if newly_generated_files:
            LOG.debug("Newly generated files:\n\t> %s", "\n\t> ".join(newly_generated_files))

        if skipped_files:
            LOG.debug("Files Skipped:\n\t> %s", "\n\t> ".join(skipped_files))

        LOG.info("'codegen' complete for '%s'", package_name)

        return output_base
コード例 #17
0
    def execute_command(self, args):
        LOG.info("Starting 'extract'...")
        LOG.debug("'extract' called with %s", args)

        # Set docgen name for SDKException
        SDKException.command_ran = self.CMD_NAME

        # Get output_base, use args.output if defined, else current directory
        output_base = args.output if args.output else os.curdir
        output_base = os.path.abspath(output_base)

        # If --exportfile is specified, read org_export from that file
        if args.exportfile:
            LOG.info("Using local export file: %s", args.exportfile)
            org_export = sdk_helpers.read_local_exportfile(args.exportfile)

        else:
            # Instantiate connection to the Resilient Appliance
            res_client = sdk_helpers.get_resilient_client(
                path_config_file=args.config)

            # Generate + get latest export from Resilient Server
            org_export = sdk_helpers.get_latest_org_export(res_client)

        LOG.info("Extracting data from export...")

        # Get extracted data from export
        extract_data = sdk_helpers.get_from_export(
            org_export,
            message_destinations=args.messagedestination,
            functions=args.function,
            workflows=args.workflow,
            rules=args.rule,
            fields=args.field,
            artifact_types=args.artifacttype,
            datatables=args.datatable,
            tasks=args.task,
            scripts=args.script,
            incident_types=args.incidenttype,
            playbooks=args.playbook)

        # Get 'minified' version of the export. This is used in to create export.res
        min_extract_data = sdk_helpers.minify_export(
            org_export,
            message_destinations=sdk_helpers.get_object_api_names(
                ResilientObjMap.MESSAGE_DESTINATIONS,
                extract_data.get("message_destinations")),
            functions=sdk_helpers.get_object_api_names(
                ResilientObjMap.FUNCTIONS, extract_data.get("functions")),
            workflows=sdk_helpers.get_object_api_names(
                ResilientObjMap.WORKFLOWS, extract_data.get("workflows")),
            rules=sdk_helpers.get_object_api_names(ResilientObjMap.RULES,
                                                   extract_data.get("rules")),
            fields=extract_data.get("all_fields"),
            artifact_types=sdk_helpers.get_object_api_names(
                ResilientObjMap.INCIDENT_ARTIFACT_TYPES,
                extract_data.get("artifact_types")),
            datatables=sdk_helpers.get_object_api_names(
                ResilientObjMap.DATATABLES, extract_data.get("datatables")),
            tasks=sdk_helpers.get_object_api_names(ResilientObjMap.TASKS,
                                                   extract_data.get("tasks")),
            phases=sdk_helpers.get_object_api_names(
                ResilientObjMap.PHASES, extract_data.get("phases")),
            scripts=sdk_helpers.get_object_api_names(
                ResilientObjMap.SCRIPTS, extract_data.get("scripts")),
            incident_types=sdk_helpers.get_object_api_names(
                ResilientObjMap.INCIDENT_TYPES,
                extract_data.get("incident_types")),
            playbooks=sdk_helpers.get_object_api_names(
                ResilientObjMap.PLAYBOOKS,
                extract_data.get(constants.CUST_PLAYBOOKS)))

        # Convert dict to JSON string
        if sys.version_info.major >= 3:
            res_data = json.dumps(min_extract_data, ensure_ascii=False)
        else:
            res_data = unicode(json.dumps(min_extract_data,
                                          ensure_ascii=False))

        # Generate path to file
        file_name = "export-{0}".format(
            sdk_helpers.get_timestamp(
                org_export.get("export_date", 0) / 1000.0))

        # If custom name supplied, prepend it
        if args.name:
            file_name = "{0}-{1}".format(args.name, file_name)

        path_file_to_write = os.path.join(output_base,
                                          "{0}.res".format(file_name))

        LOG.info("Generating %s.res", file_name)

        # Write the file
        sdk_helpers.write_file(path_file_to_write, res_data)

        LOG.debug('Wrote: %s', path_file_to_write)

        # If we should create .zip archive
        if args.zip:

            LOG.info("Generating %s.zip", file_name)

            # Get path to .zip
            path_dir_to_zip = os.path.join(output_base, file_name)

            # Create directory
            os.makedirs(path_dir_to_zip)

            # Copy the written export file into new dir
            shutil.copy(path_file_to_write, path_dir_to_zip)

            # zip the dir
            the_zip = shutil.make_archive(base_name=file_name,
                                          format="zip",
                                          root_dir=path_dir_to_zip)

            if output_base != os.path.dirname(the_zip):
                # Move the zip into the output base
                shutil.move(the_zip, output_base)

            LOG.debug('Wrote: %s.zip', path_dir_to_zip)

            # Remove directory
            shutil.rmtree(path_dir_to_zip)

        LOG.info("'extract' complete")
コード例 #18
0
    def _clone_multiple_action_objects(self, args, new_export_data,
                                       org_export):
        LOG.info("Prefix provided {}, copying multiple Action Objects".format(
            args.prefix))

        # Get data required from the export
        jinja_data = get_from_export(
            org_export,
            message_destinations=args.messagedestination,
            functions=args.function,
            workflows=args.workflow,
            rules=args.rule,
            scripts=args.script,
            get_related_objects=False)
        # Get 'minified' version of the export. This is used in customize.py
        minified = minify_export(
            org_export,
            message_destinations=get_object_api_names(
                ResilientObjMap.MESSAGE_DESTINATIONS,
                jinja_data.get("message_destinations")),
            functions=get_object_api_names(ResilientObjMap.FUNCTIONS,
                                           jinja_data.get("functions")),
            workflows=get_object_api_names(ResilientObjMap.WORKFLOWS,
                                           jinja_data.get("workflows")),
            rules=get_object_api_names(ResilientObjMap.RULES,
                                       jinja_data.get("rules")),
            scripts=get_object_api_names(ResilientObjMap.SCRIPTS,
                                         jinja_data.get("scripts")))

        # For each support object
        for object_type in ACTION_OBJECT_KEYS:
            for obj in minified.get(object_type, []):
                old_api_name = obj.get('export_key')
                new_api_name = "{}_{}".format(args.prefix, old_api_name)
                # If the object we are dealing with was one of the requested objects
                if self.action_obj_was_specified(args, obj):
                    # Ensure the new_api_name for each object is unique, raise an Exception otherwise
                    CmdClone.perform_duplication_check(
                        object_type,
                        resilient_export_obj_mapping.get(object_type),
                        "Object", new_api_name, org_export)

                    # Handle functions for cloning
                    if obj.get('display_name', False):
                        new_function = CmdClone.replace_function_object_attrs(
                            obj, new_api_name)

                        new_export_data[object_type].append(new_function)
                    # Handle workflows for cloning
                    elif obj.get('content', {}).get('xml', False):
                        new_export_data['workflows'].append(
                            CmdClone.replace_workflow_object_attrs(
                                obj, old_api_name, new_api_name, obj['name'],
                                args.changetype))
                    # Handle Message Destination. Of the supported Action Object types; only Message Destination and Workflow use programmatic_name
                    elif obj.get('programmatic_name', False):
                        if obj.get('api_keys', False) and obj.get(
                                'users', False):
                            # Save the User and API key auth for upload after initial clone
                            resilient_msg_dest_auth_info['users'].update(
                                {obj['name']: obj['users']})
                            resilient_msg_dest_auth_info['api_keys'].update(
                                {obj['name']: obj['api_keys']})
                        new_export_data['message_destinations'].append(
                            CmdClone.replace_md_object_attrs(
                                obj, new_api_name))
                    # Handle Rules and everything else
                    else:
                        new_export_data[object_type].append(
                            CmdClone.replace_rule_object_attrs(
                                obj, new_api_name, args.changetype))
コード例 #19
0
    def execute_command(self, args):
        LOG.debug("docgen called with %s", args)

        # Set docgen name for SDKException
        SDKException.command_ran = self.CMD_NAME

        # Get absolute path_to_src
        path_to_src = os.path.abspath(args.p)

        LOG.debug("Path to project: %s", path_to_src)

        # Instansiate Jinja2 Environment with path to Jinja2 templates
        jinja_env = sdk_helpers.setup_jinja_env("data/docgen/templates")

        # Load the Jinja2 Templates
        readme_template = jinja_env.get_template(README_TEMPLATE_NAME)

        # Generate path to setup.py file
        path_setup_py_file = os.path.join(path_to_src,
                                          package_helpers.BASE_NAME_SETUP_PY)

        try:
            # Ensure we have read permissions for setup.py
            sdk_helpers.validate_file_paths(os.R_OK, path_setup_py_file)
        except SDKException as err:
            err.message += "\nEnsure you are in the directory of the package you want to run docgen for"
            raise err

        # Parse the setup.py file
        setup_py_attributes = package_helpers.parse_setup_py(
            path_setup_py_file,
            package_helpers.SUPPORTED_SETUP_PY_ATTRIBUTE_NAMES)

        package_name = setup_py_attributes.get("name", "")

        # Generate paths to other required directories + files
        path_customize_py_file = os.path.join(
            path_to_src, package_name, package_helpers.PATH_CUSTOMIZE_PY)
        path_config_py_file = os.path.join(path_to_src, package_name,
                                           package_helpers.PATH_CONFIG_PY)
        path_readme = os.path.join(path_to_src,
                                   package_helpers.BASE_NAME_README)
        path_screenshots_dir = os.path.join(path_to_src,
                                            package_helpers.PATH_SCREENSHOTS)
        path_payload_samples_dir = os.path.join(
            path_to_src, package_helpers.BASE_NAME_PAYLOAD_SAMPLES_DIR)

        # Ensure we have read permissions for each required file and the file exists
        sdk_helpers.validate_file_paths(os.R_OK, path_setup_py_file,
                                        path_customize_py_file,
                                        path_config_py_file)

        # Check doc/screenshots directory exists, if not, create it + copy default screenshot
        if not os.path.isdir(path_screenshots_dir):
            os.makedirs(path_screenshots_dir)
            shutil.copy(package_helpers.PATH_DEFAULT_SCREENSHOT,
                        path_screenshots_dir)

        # Get the resilient_circuits dependency string from setup.py file
        res_circuits_dep_str = package_helpers.get_dependency_from_install_requires(
            setup_py_attributes.get("install_requires"), "resilient_circuits")

        if not res_circuits_dep_str:
            res_circuits_dep_str = package_helpers.get_dependency_from_install_requires(
                setup_py_attributes.get("install_requires"),
                "resilient-circuits")

        # Get ImportDefinition from customize.py
        customize_py_import_def = package_helpers.get_import_definition_from_customize_py(
            path_customize_py_file)

        # Parse the app.configs from the config.py file
        jinja_app_configs = package_helpers.get_configs_from_config_py(
            path_config_py_file)

        # Get field names from ImportDefinition
        field_names = []
        for f in customize_py_import_def.get("fields", []):
            f_export_key = f.get("export_key")

            if "incident/" in f_export_key and f_export_key not in IGNORED_INCIDENT_FIELDS:
                field_names.append(f.get(ResilientObjMap.FIELDS, ""))

        # Get data from ImportDefinition
        import_def_data = sdk_helpers.get_from_export(
            customize_py_import_def,
            message_destinations=sdk_helpers.get_object_api_names(
                ResilientObjMap.MESSAGE_DESTINATIONS,
                customize_py_import_def.get("message_destinations")),
            functions=sdk_helpers.get_object_api_names(
                ResilientObjMap.FUNCTIONS,
                customize_py_import_def.get("functions")),
            workflows=sdk_helpers.get_object_api_names(
                ResilientObjMap.WORKFLOWS,
                customize_py_import_def.get("workflows")),
            rules=sdk_helpers.get_object_api_names(
                ResilientObjMap.RULES, customize_py_import_def.get("actions")),
            fields=field_names,
            artifact_types=sdk_helpers.get_object_api_names(
                ResilientObjMap.INCIDENT_ARTIFACT_TYPES,
                customize_py_import_def.get("incident_artifact_types")),
            datatables=sdk_helpers.get_object_api_names(
                ResilientObjMap.DATATABLES,
                customize_py_import_def.get("types")),
            tasks=sdk_helpers.get_object_api_names(
                ResilientObjMap.TASKS,
                customize_py_import_def.get("automatic_tasks")),
            scripts=sdk_helpers.get_object_api_names(
                ResilientObjMap.SCRIPTS,
                customize_py_import_def.get("scripts")),
            playbooks=sdk_helpers.get_object_api_names(
                ResilientObjMap.PLAYBOOKS,
                customize_py_import_def.get("playbooks", [])))

        # Lists we use in Jinja Templates
        jinja_functions = self._get_function_details(
            import_def_data.get("functions", []),
            import_def_data.get("workflows", []))
        jinja_scripts = self._get_script_details(
            import_def_data.get("scripts", []))
        jinja_rules = self._get_rule_details(import_def_data.get("rules", []))
        jinja_datatables = self._get_datatable_details(
            import_def_data.get("datatables", []))
        jinja_custom_fields = self._get_custom_fields_details(
            import_def_data.get("fields", []))
        jinja_custom_artifact_types = self._get_custom_artifact_details(
            import_def_data.get("artifact_types", []))
        jinja_playbooks = self._get_playbook_details(
            import_def_data.get("playbooks", []))

        # Other variables for Jinja Templates
        package_name_dash = package_name.replace("_", "-")
        server_version = customize_py_import_def.get("server_version", {})
        supported_app = sdk_helpers.does_url_contain(
            setup_py_attributes.get("url", ""), "ibm.com/mysupport")

        # See if a payload_samples dir exists and use the contents for function results
        try:
            sdk_helpers.validate_dir_paths(os.R_OK, path_payload_samples_dir)

            for f in jinja_functions:
                fn_name = f.get("x_api_name")
                path_payload_samples_fn_name = os.path.join(
                    path_payload_samples_dir, fn_name)
                path_output_json_example = os.path.join(
                    path_payload_samples_fn_name,
                    package_helpers.BASE_NAME_PAYLOAD_SAMPLES_EXAMPLE)

                try:
                    sdk_helpers.validate_file_paths(os.R_OK,
                                                    path_output_json_example)
                    f["results"] = sdk_helpers.read_json_file(
                        path_output_json_example)
                except SDKException as e:
                    sdk_helpers.handle_file_not_found_error(
                        e,
                        u"Error getting results. No '{0}' file found for '{1}'."
                        .format(
                            package_helpers.BASE_NAME_PAYLOAD_SAMPLES_EXAMPLE,
                            fn_name))

        except SDKException as e:
            sdk_helpers.handle_file_not_found_error(
                e, u"Error getting results. No '{0}' directory found.".format(
                    package_helpers.BASE_NAME_PAYLOAD_SAMPLES_EXAMPLE))

        LOG.info("Rendering README for %s", package_name_dash)

        # Render the README Jinja2 Templeate with parameters
        rendered_readme = readme_template.render({
            "name_underscore":
            package_name,
            "name_dash":
            package_name_dash,
            "display_name":
            setup_py_attributes.get("display_name", package_name),
            "short_description":
            setup_py_attributes.get("description"),
            "long_description":
            setup_py_attributes.get("long_description"),
            "version":
            setup_py_attributes.get("version"),
            "server_version":
            server_version.get("version"),
            "all_dependencies":
            setup_py_attributes.get("install_requires", []),
            "res_circuits_dependency_str":
            res_circuits_dep_str,
            "author":
            setup_py_attributes.get("author"),
            "support_url":
            setup_py_attributes.get("url"),
            "supported_app":
            supported_app,
            "app_configs":
            jinja_app_configs[1],
            "functions":
            jinja_functions,
            "scripts":
            jinja_scripts,
            "rules":
            jinja_rules,
            "datatables":
            jinja_datatables,
            "custom_fields":
            jinja_custom_fields,
            "custom_artifact_types":
            jinja_custom_artifact_types,
            "playbooks":
            jinja_playbooks,
            "placeholder_string":
            constants.DOCGEN_PLACEHOLDER_STRING
        })

        # Create a backup if needed of README
        sdk_helpers.rename_to_bak_file(path_readme,
                                       package_helpers.PATH_DEFAULT_README)

        LOG.info("Writing README to: %s", path_readme)

        # Write the new README
        sdk_helpers.write_file(path_readme, rendered_readme)