From 7e83d0876ddb84a45e130eeba28bc40ef53c074b Mon Sep 17 00:00:00 2001 From: Yaron Yogev Date: Thu, 27 Jul 2017 09:02:54 +0300 Subject: Calipso initial release for OPNFV Change-Id: I7210c244b0c10fa80bfa8c77cb86c9d6ddf8bc88 Signed-off-by: Yaron Yogev --- app/discover/scan_metadata_parser.py | 202 +++++++++++++++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 app/discover/scan_metadata_parser.py (limited to 'app/discover/scan_metadata_parser.py') diff --git a/app/discover/scan_metadata_parser.py b/app/discover/scan_metadata_parser.py new file mode 100644 index 0000000..df27e18 --- /dev/null +++ b/app/discover/scan_metadata_parser.py @@ -0,0 +1,202 @@ +############################################################################### +# Copyright (c) 2017 Koren Lev (Cisco Systems), Yaron Yogev (Cisco Systems) # +# and others # +# # +# All rights reserved. This program and the accompanying materials # +# are made available under the terms of the Apache License, Version 2.0 # +# which accompanies this distribution, and is available at # +# http://www.apache.org/licenses/LICENSE-2.0 # +############################################################################### +from discover.fetchers.folder_fetcher import FolderFetcher +from utils.metadata_parser import MetadataParser +from utils.mongo_access import MongoAccess +from utils.util import ClassResolver + + +class ScanMetadataParser(MetadataParser): + + SCANNERS_PACKAGE = 'scanners_package' + SCANNERS_FILE = 'scanners.json' + SCANNERS = 'scanners' + + TYPE = 'type' + FETCHER = 'fetcher' + CHILDREN_SCANNER = 'children_scanner' + ENVIRONMENT_CONDITION = 'environment_condition' + OBJECT_ID_TO_USE_IN_CHILD = 'object_id_to_use_in_child' + + COMMENT = '_comment' + + REQUIRED_SCANNER_ATTRIBUTES = [TYPE, FETCHER] + ALLOWED_SCANNER_ATTRIBUTES = [TYPE, FETCHER, CHILDREN_SCANNER, + ENVIRONMENT_CONDITION, + OBJECT_ID_TO_USE_IN_CHILD] + + MECHANISM_DRIVER = 'mechanism_driver' + + def __init__(self, inventory_mgr): + super().__init__() + self.inv = inventory_mgr + self.constants = {} + + def get_required_fields(self): + return [self.SCANNERS_PACKAGE, self.SCANNERS] + + def validate_fetcher(self, scanner_name: str, scan_type: dict, + type_index: int, package: str): + fetcher = scan_type.get(self.FETCHER, '') + if not fetcher: + self.add_error('missing or empty fetcher in scanner {} type #{}' + .format(scanner_name, str(type_index))) + elif isinstance(fetcher, str): + try: + module_name = ClassResolver.get_module_file_by_class_name(fetcher) + fetcher_package = module_name.split("_")[0] + if package: + fetcher_package = ".".join((package, fetcher_package)) + instance = ClassResolver.get_instance_of_class(package_name=fetcher_package, + module_name=module_name, + class_name=fetcher) + except ValueError: + instance = None + if not instance: + self.add_error('failed to find fetcher class {} in scanner {}' + ' type #{}' + .format(fetcher, scanner_name, type_index)) + scan_type[self.FETCHER] = instance + elif isinstance(fetcher, dict): + is_folder = fetcher.get('folder', False) + if not is_folder: + self.add_error('scanner {} type #{}: ' + 'only folder dict accepted in fetcher' + .format(scanner_name, type_index)) + else: + instance = FolderFetcher(fetcher['types_name'], + fetcher['parent_type'], + fetcher.get('text', '')) + scan_type[self.FETCHER] = instance + else: + self.add_error('incorrect type of fetcher for scanner {} type #{}' + .format(scanner_name, type_index)) + + def validate_children_scanner(self, scanner_name: str, type_index: int, + scanners: dict, scan_type: dict): + scanner = scanners[scanner_name] + if 'children_scanner' in scan_type: + children_scanner = scan_type.get('children_scanner') + if not isinstance(children_scanner, str): + self.add_error('scanner {} type #{}: ' + 'children_scanner must be a string' + .format(scanner_name, type_index)) + elif children_scanner not in scanners: + self.add_error('scanner {} type #{}: ' + 'children_scanner {} not found ' + .format(scanner_name, type_index, + children_scanner)) + + def validate_environment_condition(self, scanner_name: str, type_index: int, + scanner: dict): + if self.ENVIRONMENT_CONDITION not in scanner: + return + condition = scanner[self.ENVIRONMENT_CONDITION] + if not isinstance(condition, dict): + self.add_error('scanner {} type #{}: condition must be dict' + .format(scanner_name, str(type_index))) + return + if self.MECHANISM_DRIVER in condition.keys(): + drivers = condition[self.MECHANISM_DRIVER] + if not isinstance(drivers, list): + self.add_error('scanner {} type #{}: ' + '{} must be a list of strings' + .format(scanner_name, type_index, + self.MECHANISM_DRIVER)) + if not all((isinstance(driver, str) for driver in drivers)): + self.add_error('scanner {} type #{}: ' + '{} must be a list of strings' + .format(scanner_name, type_index, + self.MECHANISM_DRIVER)) + else: + for driver in drivers: + self.validate_constant(scanner_name, + driver, + 'mechanism_drivers', + 'mechanism drivers') + + def validate_scanner(self, scanners: dict, name: str, package: str): + scanner = scanners.get(name) + if not scanner: + self.add_error('failed to find scanner: {}') + return + + # make sure only allowed attributes are supplied + for i in range(0, len(scanner)): + scan_type = scanner[i] + self.validate_scan_type(scanners, name, i+1, scan_type, package) + + def validate_scan_type(self, scanners: dict, scanner_name: str, + type_index: int, scan_type: dict, package: str): + # keep previous error count to know if errors were detected here + error_count = len(self.errors) + # ignore comments + scan_type.pop(self.COMMENT, '') + for attribute in scan_type.keys(): + if attribute not in self.ALLOWED_SCANNER_ATTRIBUTES: + self.add_error('unknown attribute {} ' + 'in scanner {}, type #{}' + .format(attribute, scanner_name, + str(type_index))) + + # make sure required attributes are supplied + for attribute in ScanMetadataParser.REQUIRED_SCANNER_ATTRIBUTES: + if attribute not in scan_type: + self.add_error('scanner {}, type #{}: ' + 'missing attribute "{}"' + .format(scanner_name, str(type_index), + attribute)) + # the following checks depend on previous checks, + # so return if previous checks found errors + if len(self.errors) > error_count: + return + + # type must be valid object type + self.validate_constant(scanner_name, scan_type[self.TYPE], + 'scan_object_types', 'types') + self.validate_fetcher(scanner_name, scan_type, type_index, package) + self.validate_children_scanner(scanner_name, type_index, scanners, + scan_type) + self.validate_environment_condition(scanner_name, type_index, + scan_type) + + def get_constants(self, scanner_name, items_desc, constant_type): + if not self.constants.get(constant_type): + constants = MongoAccess.db['constants'] + values_list = constants.find_one({'name': constant_type}) + if not values_list: + raise ValueError('scanner {}: ' + 'could not find {} list in DB' + .format(scanner_name, items_desc)) + self.constants[constant_type] = values_list + return self.constants[constant_type] + + def validate_constant(self, + scanner_name: str, + value_to_check: str, + constant_type: str, + items_desc: str = None): + values_list = self.get_constants(scanner_name, items_desc, + constant_type) + values = [t['value'] for t in values_list['data']] + if value_to_check not in values: + self.add_error('scanner {}: value not in {}: {}' + .format(scanner_name, items_desc, value_to_check)) + + def validate_metadata(self, metadata: dict) -> bool: + super().validate_metadata(metadata) + scanners = metadata.get(self.SCANNERS, {}) + package = metadata.get(self.SCANNERS_PACKAGE) + if not scanners: + self.add_error('no scanners found in scanners list') + else: + for name in scanners.keys(): + self.validate_scanner(scanners, name, package) + return len(self.errors) == 0 -- cgit 1.2.3-korg