def _add_to_collection(self, service_class): """ Add a service to the database. """ logger.info("Adding service %s to MongoDB" % service_class.name) description = inspect.getdoc(service_class) config = service_class.build_default_config() config = AnalysisConfig(**config) new_service = CRITsService() new_service.name = service_class.name new_service.version = service_class.version new_service.service_type = service_class.type_ new_service.purpose = service_class.purpose new_service.rerunnable = service_class.rerunnable new_service.supported_types = service_class.supported_types new_service.required_fields = service_class.required_fields new_service.enabled = False new_service.run_on_triage = False new_service.description = description new_service.config = config try: new_service.save() logger.debug('Added service %s successfully.' % service_class.name) except ValidationError, e: logger.warning('Failed to add service %s: %s' % (service_class.name, e))
def _register_services(self, klass): """ Create a dict with names of available services and classes that implement them. This is a recursive function since __subclasses__() only returns direct subclasses. If class A(object):, class B(A):, and class C(B):, then A.__subclasses__() doesn't contain C. All subclasses of the Service class are saved in the `services` dictionary. It is intended that each of these was imported by the _import_services function, but this is not enforced. The key in the dictionary is the `name` class-level field, and the value is the class itself. It is recommended that the service "example" be implemented in a class "ExampleService" defined in a module named "example_service", but this is not enforced, and the only string visible to the end-user/analyst is the service name. """ for service_class in klass.__subclasses__(): # TODO: replace this with a proper check for a valid service if not (hasattr(service_class, "name") and hasattr(service_class, "version")): # If this is a subclass of Service but not an actual service # call this function recursively. self._register_services(service_class) continue service_name = service_class.name service_version = service_class.version service_description = service_class.description supported_types = service_class.supported_types compatability_mode = service_class.compatability_mode logger.debug("Found service subclass: %s version %s" % (service_name, service_version)) try: StrictVersion(service_version) except ValueError as e: # Unable to parse the service version msg = ("Service %s is invalid, and will not be available." % service_name) logger.warning(msg) logger.warning(e) continue else: # Only register the service if it is valid. logger.debug("Registering Service %s" % service_name) svc_obj = CRITsService.objects(name=service_class.name).first() service = service_class() if not svc_obj: svc_obj = CRITsService() svc_obj.name = service_name try: new_config = service.get_config({}) svc_obj.config = AnalysisConfig(**new_config) except ServiceConfigError: svc_obj.status = "misconfigured" msg = ("Service %s is misconfigured." % service_name) logger.warning(msg) else: svc_obj.status = "available" else: existing_config = svc_obj.config.to_dict() try: new_config = service.get_config(existing_config) svc_obj.config = AnalysisConfig(**new_config) except ServiceConfigError: svc_obj.status = "misconfigured" svc_obj.enabled = False svc_obj.run_on_triage = False msg = ("Service %s is misconfigured." % service_name) logger.warning(msg) else: svc_obj.status = "available" # Give the service a chance to tell us what is wrong with the # config. try: service.parse_config(svc_obj.config.to_dict()) except ServiceConfigError as e: svc_obj.status = "misconfigured" svc_obj.enabled = False svc_obj.run_on_triage = False svc_obj.description = service_description svc_obj.version = service_version svc_obj.supported_types = supported_types svc_obj.compatability_mode = compatability_mode svc_obj.save() self._services[service_class.name] = service_class # For anything in the database that did not import properly, mark the # status to unavailable. svcs = CRITsService.objects() for svc in svcs: if svc.name not in self._services: svc.status = 'unavailable' svc.enabled = False svc.run_on_triage = False svc.save()