示例#1
0
    def fill(self, init=None, yes=False, reinit_auth=False):
        self.__put_canopsis_document()

        tools = []

        for module in self.modules:
            try:
                migrationcls = lookup(module)

            except ImportError as err:
                self.logger.error(
                    'Impossible to load module "{0}": {1}'.format(module, err))

                continue

            migrationtool = migrationcls()
            migrationtool.logger.addHandler(self.loghandler)
            tools.append(migrationtool)

        coll = None
        if init is None:
            store = MongoStore.get_default()
            store.authenticate()
            coll = MongoCollection(store.get_collection(self.FLAG_COLLECTION))

            data = coll.find_one({"_id": self.FLAG_COLLECTION})
            if data is None:
                print("Database not intialized. Initializing...")
                init = True
            else:
                print("Database already intialized. Updating...")
                init = False

        if init is None and reinit_auth is False:
            data = {
                "_id": "initialized",
                "at": str(time.strftime("%a, %d %b %Y %H:%M:%S +0000"))
            }
            print("The canopsis initialization flag did not exist in the "
                  "database. So canopsinit will (re?)initialized the "
                  "database. Meaning, it may delete some important data  "
                  "from canopsis database. If you still want to initialize "
                  "the database, call the same command with the "
                  "`--authorize-reinit` flag. Or if you do not want to "
                  "initialize the database, add the document `{0}` in the {1} "
                  "collections.".format(data, self.FLAG_COLLECTION))
            exit(1)

        for tool in tools:
            if init:
                tool.init(yes=yes)

            else:
                tool.update(yes=yes)

        if init is True:
            coll.insert({
                "_id": self.FLAG_COLLECTION,
                "at": str(time.strftime("%a, %d %b %Y %H:%M:%S +0000"))
            })
示例#2
0
class TestMongoCollection(unittest.TestCase):
    def setUp(self):
        output = StringIO()
        self.logger = Logger.get('test', output, OutputStream)

        self.storage = Middleware.get_middleware_by_uri(
            'storage-default-testmongocollection://')

        self.collection = MongoCollection(collection=self.storage._backend,
                                          logger=self.logger)

        self.id_ = 'testid'

    def tearDown(self):
        """Teardown"""
        self.collection.remove()

    def test_insert(self):
        res = self.collection.insert(document={'_id': self.id_})
        self.assertEqual(res, self.id_)

        res2 = self.collection.insert(document={'up': 'down'})
        self.assertTrue(isinstance(res, string_types))
        self.assertNotEqual(res, res2)

    def test_update(self):
        res = self.collection.update(query={'_id': self.id_},
                                     document={'strange': 'charm'})
        self.assertTrue(MongoCollection.is_successfull(res))
        self.assertEqual(res['n'], 0)

        res = self.collection.update(query={'_id': self.id_},
                                     document={'yin': 'yang'},
                                     upsert=True)
        self.assertTrue(MongoCollection.is_successfull(res))
        self.assertEqual(res['n'], 1)

        res = self.collection.find_one(self.id_)
        self.assertEqual(res['yin'], 'yang')
        self.assertTrue('strange' not in res)

    def test_remove(self):
        res = self.collection.insert(document={
            '_id': self.id_,
            'top': 'bottom'
        })
        self.assertIsNotNone(res)

        res = self.collection.remove(query={'_id': self.id_})
        self.assertTrue(MongoCollection.is_successfull(res))
        self.assertEqual(res['n'], 1)

        # Deleting non-existing object doesn't throw error
        res = self.collection.remove(query={})
        self.assertTrue(MongoCollection.is_successfull(res))
        self.assertEqual(res['n'], 0)

    def test_find(self):
        res = self.collection.insert(document={'_id': self.id_, 'yin': 'yang'})
        self.assertIsNotNone(res)

        res = self.collection.find(query={'_id': self.id_})
        self.assertTrue(isinstance(res, Cursor))
        res = list(res)
        self.assertTrue(isinstance(res, list))
        self.assertEqual(res[0]['yin'], 'yang')

    def test_find_one(self):
        res = self.collection.insert(document={'_id': self.id_, 'up': 'down'})
        self.assertIsNotNone(res)
        res = self.collection.insert(document={'strange': 'charm'})
        self.assertIsNotNone(res)

        res = self.collection.find_one(query={'_id': self.id_})
        self.assertTrue(isinstance(res, dict))
        self.assertEqual(res['up'], 'down')

    def test_is_successfull(self):
        dico = {'ok': 1.0, 'n': 2}
        self.assertTrue(MongoCollection.is_successfull(dico))

        dico = {'ok': 666.667, 'n': 1}
        self.assertFalse(MongoCollection.is_successfull(dico))

        dico = {'n': 2}
        self.assertFalse(MongoCollection.is_successfull(dico))
示例#3
0
class RuleManager(object):
    """
    Manager for event filter rules.
    """
    def __init__(self, logger):
        self.logger = logger
        self.rule_collection = MongoCollection(
            MongoStore.get_default().get_collection(RULE_COLLECTION))

    def get_by_id(self, rule_id):
        """
        Get an event filter rule given its id.

        :param str rule_id: the id of the rule.
        :rtype: Dict[str, Any]
        """
        return self.rule_collection.find_one({
            RuleField.id: rule_id
        })

    def create(self, rule):
        """
        Create a new rule and return its id.

        :param Dict[str, Any] rule:
        :rtype: str
        :raises: InvalidRuleError if the rule is invalid. CollectionError if
        the creation fails.
        """
        rule_id = str(uuid4())

        rule[RuleField.id] = rule_id
        self.validate(rule_id, rule)

        self.rule_collection.insert(rule)
        return rule_id

    def remove_with_id(self, rule_id):
        """
        Remove a rule given its id.

        :param str rule_id: the id of the rule. CollectionError if the
        creation fails.
        """
        self.rule_collection.remove({
            RuleField.id: rule_id
        })

    def list(self):
        """
        Return a list of all the rules.

        :rtype: List[Dict[str, Any]]
        """
        return list(self.rule_collection.find({}))

    def update(self, rule_id, rule):
        """
        Update a rule given its id.

        :param str rule_id: the id of the rule.
        :param Dict[str, Any] rule:
        :raises: InvalidRuleError if the rule is invalid. CollectionError if
        the creation fails.
        """
        self.validate(rule_id, rule)

        self.rule_collection.update({
            RuleField.id: rule_id
        }, rule, upsert=False)

    def validate(self, rule_id, rule):
        """
        Check that the rule is valid.

        The pattern and external_data fields are not validated by this method.

        :param Dict[str, Any] view:
        :raises: InvalidRuleError if it is invalid.
        """
        # Validate id field
        if rule.get(RuleField.id, rule_id) != rule_id:
            raise InvalidRuleError(
                'The {0} field should not be modified.'.format(RuleField.id))

        # Check that there are no unexpected fields in the rule
        unexpected_fields = set(rule.keys()).difference(RuleField.values)
        if unexpected_fields:
            raise InvalidRuleError(
                'Unexpected fields: {0}.'.format(', '.join(unexpected_fields)))

        # Validate the type field
        if RuleField.type not in rule:
            raise InvalidRuleError(
                'The {0} field is required.'.format(RuleField.type))

        if rule.get(RuleField.type) not in RuleType.values:
            raise InvalidRuleError(
                'The {0} field should be one of: {1}.'.format(
                    RuleField.type,
                    ', '.join(RuleType.values)))

        # Validate the priority field
        if not isinstance(rule.get(RuleField.priority, 0), int):
            raise InvalidRuleError(
                'The {0} field should be an integer.'.format(
                    RuleField.priority))

        # Validate the enabled field
        if not isinstance(rule.get(RuleField.enabled, True), bool):
            raise InvalidRuleError(
                'The {0} field should be a boolean.'.format(
                    RuleField.enabled))

        if rule.get(RuleField.type) != RuleType.enrichment:
            # Check that the enrichment fields are not defined for
            # non-enrichment rules.
            unexpected_fields = set(rule.keys()).intersection(
                ENRICHMENT_FIELDS)
            if unexpected_fields:
                raise InvalidRuleError(
                    'The following fields should only be defined for '
                    'enrichment rules: {0}.'.format(
                        ', '.join(unexpected_fields)))

        else:
            # Validate the actions field of the enrichment rules.
            if RuleField.actions not in rule:
                raise InvalidRuleError(
                    'The {0} field is required for enrichment rules.'.format(
                        RuleField.actions))

            if not isinstance(rule.get(RuleField.actions), list):
                raise InvalidRuleError(
                    'The {0} field should be a list.'.format(
                        RuleField.actions))

            if not rule.get(RuleField.actions):
                raise InvalidRuleError(
                    'The {0} field should contain at least one action.'.format(
                        RuleField.actions))

            # Validate the on_success field of the enrichment rules.
            outcome = rule.get(RuleField.on_success)
            if outcome and outcome not in RuleOutcome.values:
                raise InvalidRuleError(
                    'The {0} field should be one of: {1}.'.format(
                        RuleField.on_success,
                        ', '.join(RuleOutcome.values)))

            # Validate the on_failure field of the enrichment rules.
            outcome = rule.get(RuleField.on_failure)
            if outcome and outcome not in RuleOutcome.values:
                raise InvalidRuleError(
                    'The {0} field should be one of: {1}.'.format(
                        RuleField.on_failure,
                        ', '.join(RuleOutcome.values)))
示例#4
0
class HeartbeatManager(object):
    """
    Heartbeat service manager abstraction.
    """

    COLLECTION = 'heartbeat'

    LOG_PATH = 'var/log/heartbeat.log'

    @classmethod
    def provide_default_basics(cls):
        """
        Provide logger, config, storages...

        ! Do not use in tests !

        :rtype: Union[logging.Logger,
                      canopsis.common.collection.MongoCollection]
        """
        store = MongoStore.get_default()
        collection = store.get_collection(name=cls.COLLECTION)
        return (Logger.get('action',
                           cls.LOG_PATH), MongoCollection(collection))

    def __init__(self, logger, collection):
        """

        :param `~.logger.Logger` logger: object.
        :param `~.common.collection.MongoCollection` collection: object.
        """
        self.__logger = logger
        self.__collection = MongoCollection(collection)

    def create(self, heartbeat):
        """
        Create a new Heartbeat.

        :param `HeartBeat` heartbeat: a Heartbeat model.

        :returns: a created Heartbeat ID.
        :rtype: `str`.

        :raises: (`.HeartbeatPatternExistsError`,
                  `pymongo.errors.PyMongoError`,
                  `~.common.collection.CollectionError`, ).
        """
        if self.get(heartbeat.id):
            raise HeartbeatPatternExistsError()

        return self.__collection.insert(heartbeat.to_dict())

    def get(self, heartbeat_id=None):
        """
        Get Heartbeat by ID or a list of Heartbeats
        when calling with default arguments.

        :param `Optional[str]` heartbeat_id:
        :returns: list of Heartbeat documents if **heartbeat_id** is None
                  else single Heartbeat document or None if not found.
        :raises: (`pymongo.errors.PyMongoError`, ).
        """
        if heartbeat_id:
            return self.__collection.find_one({"_id": heartbeat_id})
        return [x for x in self.__collection.find({})]

    def delete(self, heartbeat_id):
        """
        Delete Heartbeat by ID.

        :param `str` heartbeat_id: Heartbeat ID.
        :return:
        :raises: (`~.common.collection.CollectionError`, ).
        """
        return self.__collection.remove({"_id": heartbeat_id})
示例#5
0
class ViewAdapter(object):
    """
    Adapter for the view collection.
    """
    def __init__(self, logger):
        self.logger = logger
        self.view_collection = MongoCollection(
            MongoStore.get_default().get_collection(VIEWS_COLLECTION))
        self.group_collection = MongoCollection(
            MongoStore.get_default().get_collection(GROUPS_COLLECTION))

    def get_by_id(self, view_id):
        """
        Get a view given its id.

        :param str view_id: the id of the view.
        """
        return self.view_collection.find_one({ViewField.id: view_id})

    def create(self, view):
        """
        Create a new view and return its id.

        :param Dict[str, Any] view:
        :rtype: str
        """
        view_id = str(uuid4())

        view[ViewField.id] = view_id
        self.validate(view_id, view)

        self.view_collection.insert(view)
        return view_id

    def update(self, view_id, view):
        """
        Update a view given its id.

        :param str view_id: the id of the view.
        :param Dict[str, Any] view:
        """
        self.validate(view_id, view)

        self.view_collection.update({ViewField.id: view_id},
                                    view,
                                    upsert=False)

    def remove_with_id(self, view_id):
        """
        Remove a view given its id.

        :param str view_id: the id of the view.
        """
        self.view_collection.remove({ViewField.id: view_id})

    def list(self, name=None, title=None):
        """
        Return a list of views, optionally filtered by name or title.

        :param str name:
        :param str title:
        :rtype: List[Dict[str, Any]]
        :raises: InvalidFilterError
        """
        view_filter = {}

        if name is not None and title is not None:
            raise InvalidFilterError(
                'Cannot filter both by name and by title.')

        if name is not None:
            view_filter[ViewField.name] = name
        if title is not None:
            view_filter[ViewField.title] = title

        views = self.view_collection.find(view_filter)
        groups = self.group_collection.find({})

        response_groups = {}

        for group in groups:
            group_id = group[GroupField.id]

            del group[GroupField.id]
            group[GroupField.views] = []

            response_groups[group_id] = group

        for view in views:
            try:
                group_id = view[ViewField.group_id]
            except KeyError:
                # This should never happen as long as the collections are only
                # modified using the API
                self.logger.exception(
                    'The view {0} is missing the group_id field.'.format(
                        view[ViewField.id]))
                continue

            try:
                response_groups[group_id][GroupField.views].append(view)
            except KeyError:
                # This should never happen as long as the collections are only
                # modified using the API
                self.logger.exception('No group with id: {0}'.format(group_id))

        return {ViewResponseField.groups: response_groups}

    def validate(self, view_id, view):
        """
        Check that the view is valid, return InvalidViewError if it is not.

        :param Dict[str, Any] view:
        """
        # Validate id field
        if view.get(ViewField.id, view_id) != view_id:
            raise InvalidViewError(
                'The {0} field should not be modified'.format(ViewField.id))

        # Validate group_id field
        if ViewField.group_id not in view:
            raise InvalidViewError('The view should have a group_id field.')

        group_id = view[ViewField.group_id]
        group = self.group_collection.find_one({GroupField.id: group_id})
        if not group:
            raise InvalidViewError('No group with id: {0}'.format(group_id))

        # Validate name field
        if ViewField.name not in view:
            raise InvalidViewError('The view should have a name field.')

        view_name = view[ViewField.name]
        same_name_view = self.view_collection.find_one({
            ViewField.id: {
                '$ne': view_id
            },
            ViewField.name:
            view_name
        })
        if same_name_view:
            raise InvalidViewError(
                'There is already a view with the name: {0}'.format(view_name))

        # Validate title field
        if ViewField.title not in view:
            raise InvalidViewError('The view should have a title field.')
示例#6
0
class GroupAdapter(object):
    """
    Adapter for the group collection.
    """
    def __init__(self, logger):
        self.logger = logger
        self.group_collection = MongoCollection(
            MongoStore.get_default().get_collection(GROUPS_COLLECTION))
        self.view_collection = MongoCollection(
            MongoStore.get_default().get_collection(VIEWS_COLLECTION))

    def get_by_id(self, group_id, name=None, title=None):
        """
        Get a group given its id.

        :param str group_id: the id of the group.
        :param str name:
        :param str title:
        :rtype: Dict[str, Any]
        """
        group = self.group_collection.find_one({GroupField.id: group_id})

        if group:
            group[GroupField.views] = self.get_views(group_id, name, title)

        return group

    def get_views(self, group_id, name=None, title=None):
        """
        Returns the list of views of a group, optionally filtered by name or
        title.

        :param str group_id:
        :param str name:
        :param str title:
        :rtype: List[Dict[str, Any]]
        :raises: InvalidFilterError
        """
        view_filter = {ViewField.group_id: group_id}

        if name is not None and title is not None:
            raise InvalidFilterError(
                'Cannot filter both by name and by title.')

        if name is not None:
            view_filter[ViewField.name] = name
        if title is not None:
            view_filter[ViewField.title] = title

        return list(self.view_collection.find(view_filter))

    def is_empty(self, group_id):
        """
        Return True if a group is empty.

        :param str group_id:
        :rtype: bool
        """
        return self.view_collection.find({
            ViewField.group_id: group_id
        }).limit(1).count() == 0

    def create(self, group):
        """
        Create a new group.

        :param Dict group:
        :rtype: str
        :raises: InvalidGroupError
        """
        group_id = str(uuid4())

        group[GroupField.id] = group_id
        self.validate(group_id, group)

        self.group_collection.insert(group)
        return group_id

    def update(self, group_id, group):
        """
        Update a group given its id.

        :param str group_id:
        :param Dict group:
        :raises: InvalidGroupError
        """
        self.validate(group_id, group)

        self.group_collection.update({GroupField.id: group_id},
                                     group,
                                     upsert=False)

    def remove_with_id(self, group_id):
        """
        Remove a group given its id.

        :param str group_id:
        :raises: NonEmptyGroupError
        """
        if not self.is_empty(group_id):
            raise NonEmptyGroupError()

        self.group_collection.remove({GroupField.id: group_id})

    def list(self, name=None):
        """
        Return a list of groups, optionally filtered by name.

        :param str name:
        :rtype: List[Dict[str, Any]]
        """
        group_filter = {}

        if name is not None:
            group_filter[GroupField.name] = name

        return list(self.group_collection.find(group_filter))

    def validate(self, group_id, group):
        """
        Check that the gorup is valid, return InvalidGroupError if it is not.

        :param Dict[str, Any] view:
        :raises: InvalidGroupError
        """
        if group.get(GroupField.id, group_id) != group_id:
            raise InvalidGroupError(
                'The {0} field should not be modified'.format(GroupField.id))

        if GroupField.name not in group:
            raise InvalidGroupError('The group should have a name field.')

        group_name = group[GroupField.name]
        same_name_group = self.group_collection.find_one({
            GroupField.id: {
                '$ne': group_id
            },
            GroupField.name:
            group_name
        })
        if same_name_group:
            raise InvalidGroupError(
                'There is already a group with the name: {0}'.format(
                    group_name))

    def exists(self, group_id):
        """
        Return True if a group exists.

        :param str group_id:
        :rtype: bool
        """
        group = self.group_collection.find_one({GroupField.id: group_id})
        return group is not None
示例#7
0
    def fill(self, init=None, yes=False, reinit_auth=False):
        self.__put_canopsis_version_document()

        tools = []

        for module in self.modules:
            try:
                migrationcls = lookup(module)

            except ImportError as err:
                self.logger.error(
                    'Impossible to load module "{0}": {1}'.format(
                        module,
                        err
                    )
                )

                continue

            migrationtool = migrationcls()
            migrationtool.logger.addHandler(self.loghandler)
            tools.append(migrationtool)

        coll = None
        if init is None:
            store = MongoStore.get_default()
            store.authenticate()
            coll = MongoCollection(store.get_collection(self.FLAG_COLLECTION))

            data = coll.find_one({"_id": self.FLAG_COLLECTION})
            if data is None:
                print("Database not intialized. Initializing...")
                init = True
            else:
                print("Database already intialized. Updating...")
                init = False

        if init is None and reinit_auth is False:
            data = {
                "_id": "initialized",
                "at": str(time.strftime("%a, %d %b %Y %H:%M:%S +0000"))
            }
            print("The canopsis initialization flag did not exist in the "
                  "database. So canopsinit will (re?)initialized the "
                  "database. Meaning, it may delete some important data  "
                  "from canopsis database. If you still want to initialize "
                  "the database, call the same command with the "
                  "`--authorize-reinit` flag. Or if you do not want to "
                  "initialize the database, add the document `{0}` in the {1} "
                  "collections.".format(data, self.FLAG_COLLECTION))
            exit(1)

        for tool in tools:
            if init:
                tool.init(yes=yes)

            else:
                tool.update(yes=yes)

        if init is True:
            coll.insert({"_id": self.FLAG_COLLECTION,
                         "at": str(time.strftime(
                             "%a, %d %b %Y %H:%M:%S +0000"))})