Ejemplo n.º 1
0
    def delete(self, probe_name):
        """
        Delete mapped services from probe.
        """
        session = config.session()

        try:
            probe = session.query(Probe).filter(Probe.name == probe_name).one()

            logging.info("Deleting service mapping where id in %s" %
                         ",".join(request.args.getlist("id")))
            for service in session.query(MappedService)\
                    .join(Service, Service.id == MappedService.probe_service_id)\
                    .filter(MappedService.id.in_(request.args.getlist("id")))\
                    .filter(Service.probe_id == probe.id).all():

                session.delete(service)

            session.commit()

            return {"status": "OK"}
        except:
            session.rollback()
            raise

        finally:
            session.close()
Ejemplo n.º 2
0
Archivo: probe.py Proyecto: niximor/mon
 def get(self, name):
     """
     Return probe configuration.
     """
     session = config.session()
     try:
         probe = session.query(entity.Probe).filter_by(name=name).one()
         return {
             "name":
             probe.name,
             "services": [{
                 "name":
                 service.name,
                 "description":
                 service.description,
                 "deleted":
                 service.deleted,
                 "options": [{
                     "identifier": option.identifier,
                     "name": option.name,
                     "type": option.data_type,
                     "description": option.description,
                     "required": option.required
                 } for option in service.options]
             } for service in probe.services]
         }
     finally:
         session.commit()
Ejemplo n.º 3
0
Archivo: const.py Proyecto: niximor/mon
    def load_errors(self):
        """
        Load error causes from database.
        """
        session = config.session()
        try:
            errors = session.query(ErrorCause).all()

            self._errors = {}

            for error in errors:
                self._errors[error.id] = error.name
                self._errors[error.name] = error.id
        finally:
            session.close()
Ejemplo n.º 4
0
Archivo: const.py Proyecto: niximor/mon
    def load_status(self):
        """
        Load statuses from database.
        """
        session = config.session()
        try:
            statuses = session.query(Status).all()

            self._status = {}

            for status in statuses:
                self._status[status.id] = status.name
                self._status[status.name] = status.id
        finally:
            session.close()
Ejemplo n.º 5
0
Archivo: const.py Proyecto: niximor/mon
    def load_service_status(self):
        """
        Load service statuses.
        """
        session = config.session()
        try:
            statuses = session.query(ServiceStatus).order_by(ServiceStatus.id).all()

            self._service_status = {}

            for status in statuses:
                self._service_status[status.id] = status.name
                self._service_status[status.name] = status.id

        finally:
            session.close()
Ejemplo n.º 6
0
Archivo: probe.py Proyecto: niximor/mon
    def get(self):
        """
        List of probes.
        """
        session = config.session()
        try:
            query = select(session, request.args.getlist("show"),
                           self.SELECT_COLUMN_MAPPING,
                           self.SELECT_JOIN_MAPPING,
                           self.TABLE_JOINS).order_by(entity.Probe.name)

            result = [probe._asdict() for probe in query.all()]
            logging.info(result)
            return result
        finally:
            session.commit()
Ejemplo n.º 7
0
    def put(self, probe_name):
        """
        Put new reading to database.
        :param probe_name: Name of probe which sent the reading.
        """
        session = config.session()
        try:
            probe = session.query(Probe).filter(Probe.name == probe_name).one()

            # Load all active service IDs to be able to verify if posted service can be updated.
            active_service_ids = []
            active_services = {}

            for service in session.query(MappedService)\
                    .select_from(MappedService)\
                    .join(Service, MappedService.probe_service_id == Service.id)\
                    .filter(Service.probe_id == probe.id)\
                    .filter(MappedService.status_id == const.status["active"])\
                    .all():
                active_service_ids.append(service.id)
                active_services[service.id] = service

            readings_by_service = {}

            valid_service_ids = [
                value["service"] for value in request.json
                if value["service"] in active_service_ids
            ]

            # Construct thresholds[mapping_id][reading_name] = [threshold1, threshold2, ...].
            thresholds_for_mapping = {}
            for threshold, mapped_service_id in session.query(ServiceThreshold, MappedService.id)\
                    .select_from(MappedService)\
                    .join(Service, Service.id == MappedService.probe_service_id)\
                    .join(ServiceThreshold, ServiceThreshold.probe_service_id == Service.id)\
                    .order_by(MappedService.id, ServiceThreshold.service_status_id)\
                    .filter(MappedService.id.in_(valid_service_ids)).all():
                thresholds_for_mapping\
                    .setdefault(mapped_service_id, {})\
                    .setdefault(threshold.reading, [])\
                    .append(threshold)

            for reading in session.query(Reading)\
                    .filter(Reading.mapped_service_id.in_(active_service_ids))\
                    .all():
                readings_by_service.setdefault(reading.mapped_service_id,
                                               {})[reading.name] = reading

            for value in request.json:
                if value["service"] not in active_service_ids:
                    logging.warning(
                        "Received reading for unknown service %s. Maybe it was removed or deactivated. "
                        "Ignoring.")
                    continue

                # Test if reading value already exists
                db_reading = readings_by_service.setdefault(
                    value["service"], {}).get(value["reading"])
                if db_reading is None:
                    db_reading = Reading(mapped_service_id=value["service"],
                                         name=value["reading"])
                    readings_by_service[value["service"]][
                        value["reading"]] = db_reading
                    logging.debug("Create new reading %s." %
                                  (value["reading"], ))

                db_reading.values.append(
                    ReadingValue(datetime=value["timestamp"],
                                 value=value["value"]))
                logging.debug("Store value %s=%s." %
                              (db_reading.name, value["value"]))

                # TDetermine whether service changes status and write that to database.
                db_service = active_services[value["service"]]
                if value["service"] in thresholds_for_mapping:
                    # Determine what is current status of service.

                    thresholds = thresholds_for_mapping[value["service"]]

                    combined_thresholds = {}

                    # Determine possible keys and sort them by priority.
                    for reading, status_thresholds in thresholds.items():
                        if fnmatch(value["reading"], reading):
                            # Determine match priority. Be dummy here and think, that longer pattern is more accurate.
                            priority = len(reading)
                            for threshold in status_thresholds:
                                if threshold.service_status_id not in combined_thresholds or \
                                        combined_thresholds[threshold.service_status_id][0] < priority:
                                    combined_thresholds[
                                        threshold.
                                        service_status_id] = priority, threshold

                    # OK is default status.
                    current_status = const.service_status["ok"]
                    for key in sorted(combined_thresholds.keys()):
                        threshold = combined_thresholds[key][1]

                        if (threshold.min is not None
                                and value["value"] < threshold.min) or (
                                    threshold.max is not None
                                    and value["value"] > threshold.max):
                            current_status = threshold.service_status_id

                    if db_service.current_status != current_status:
                        db_service.current_status = current_status
                        db_service.current_status_from = now()

                        # Create history entry.
                        session.add(
                            ServiceStatusHistory(
                                mapped_service_id=db_service.id,
                                service_status_id=current_status,
                                timestamp=now()))
                        session.add(db_service)
                else:
                    if db_service.current_status is not None:
                        db_service.current_status = None
                        db_service.current_status_from = None

                        # Create history entry.
                        session.add(
                            ServiceStatusHistory(
                                mapped_service_id=db_service.id,
                                service_status_id=None,
                                timestamp=now()))
                        session.add(db_service)

                session.add(db_reading)

            session.commit()

            return {"status": "OK"}
        except:
            session.rollback()
            raise
        finally:
            session.close()
Ejemplo n.º 8
0
Archivo: probe.py Proyecto: niximor/mon
    def put(self):
        """
        Create/update probe data.
        """
        data = request.json

        session = config.session()
        try:
            probe = session.query(
                entity.Probe).filter_by(name=data["name"]).one()

            services_by_name = {}

            for service in probe.services:
                services_by_name[service.name] = service

            reported_services_names = []

            for service in data.get("services", []):
                if service["name"] not in services_by_name:
                    db_service = entity.Service(name=service["name"],
                                                description=service.get(
                                                    "description", ""))
                    probe.services.append(db_service)
                    services_by_name[db_service.name] = db_service
                else:
                    db_service = services_by_name[service["name"]]
                    db_service.description = service.get("description", "")
                    db_service.deleted = False

                reported_services_names.append(service["name"])

                options_by_identifier = {}
                reported_option_identifiers = []

                for option in db_service.options:
                    options_by_identifier[option.identifier] = option

                required_options = []
                new_required_option = False

                for option in service.get("options", []):
                    reported_option_identifiers.append(option["identifier"])
                    if option["identifier"] not in options_by_identifier:
                        db_option = entity.ServiceOption(
                            identifier=option["identifier"],
                            name=option.get("name", option["identifier"]),
                            description=option.get("description", ""),
                            data_type=option.get("type", "string"),
                            required=option.get("required", False))
                        db_service.options.append(db_option)

                        # Set all mapped services nonfunctional if new required option is introduced.
                        if db_option.required and db_service.id:
                            new_required_option = True
                    else:
                        db_option = options_by_identifier[option["identifier"]]
                        db_option.name = option.get("name",
                                                    option["identifier"])
                        db_option.description = option.get("description", "")
                        db_option.data_type = option.get("type", "string")
                        db_option.required = option.get("required", False)

                        if db_option.required and db_service.id:
                            required_options.append(db_option.id)

                # Set service error if there is new required option. First case is when there is whole new option,
                # second is for situations, when option becomes required.
                if new_required_option or db_service.deleted:
                    session.execute(
                        """UPDATE mapped_services
                                    SET status_id = :status_id, error_cause_id = :error_cause_id
                                    WHERE probe_service_id = :service_id""", {
                            "status_id":
                            const.status["error"],
                            "error_cause_id":
                            (const.error_cause["ERROR_MISSING_REQUIRED_OPTION"]
                             if new_required_option else
                             const.error_cause["ERROR_SERVICE_UNAVAILABLE"]),
                            "service_id":
                            db_service.id
                        })
                elif required_options:
                    session.execute(
                        """UPDATE mapped_services m
                                    LEFT JOIN mapped_service_options o
                                        ON (o.mapped_service_id = m.id AND o.option_id IN ("""
                        + ",".join(map(str, required_options)) + """))
                                    SET m.status_id = :status_id, m.error_cause_id = :error_cause_id
                                    WHERE o.id IS NULL""", {
                            "status_id":
                            const.status["error"],
                            "error_cause_id":
                            const.error_cause["ERROR_MISSING_REQUIRED_OPTION"],
                            "service_id":
                            db_service.id
                        })

                # Delete no longer known options.
                for option_identifier, option in options_by_identifier.items():
                    if option_identifier not in reported_option_identifiers:
                        session.delete(option)

                # Update / create thresholds.
                known_thresholds = []
                for threshold in db_service.thresholds:
                    known_thresholds.append(threshold)

                for name, limits in service.get("thresholds", {}).items():
                    found = False
                    for db_threshold in known_thresholds:
                        if db_threshold.reading == name and \
                                const.service_status[db_threshold.service_status_id] == limits["status"]:
                            found = True

                            # Update only if the limits come from service and are not overwritten by the configuration.
                            if db_threshold.source == "service":
                                db_threshold.min = limits.get("min", None)
                                db_threshold.max = limits.get("max", None)
                            break

                    if not found:
                        db_service.thresholds.append(
                            entity.ServiceThreshold(
                                service_status_id=const.service_status[
                                    limits["status"]],
                                reading=name,
                                min=limits.get("min", None),
                                max=limits.get("max", None),
                                source="service"))

            # Delete no longer known services
            for service_name, service in services_by_name.items():
                if service_name not in reported_services_names:
                    service.deleted = True

            session.add(probe)
        except Exception:
            session.rollback()
            raise
        finally:
            session.commit()

        return {"status": "OK"}
Ejemplo n.º 9
0
    def get(self, probe_name):
        """
        List mapped services of probe.
        :param probe_name: Probe name
        """
        session = config.session()

        try:
            probe = session.query(Probe).filter_by(name=probe_name).one()

            allowed_statuses = []
            for status in request.args.getlist("status"):
                if status == "all":
                    allowed_statuses = None
                    break

                if status in const.status.keys():
                    allowed_statuses.append(const.status[status])
                else:
                    raise BadRequest(
                        "Bad status value: '%s'. Must be one of %s." %
                        (status, ",".join(const.status.keys())))

            if not allowed_statuses and allowed_statuses is not None:
                allowed_statuses.append(const.status["active"])

            show = request.args.getlist("show")

            show_options = []

            for column in show:
                if column.startswith("options."):
                    show_options.append(column[len("options."):])

            for column in show_options:
                show.remove("options.%s" % (column, ))

            mapped_services = select(session, show, self.SHOW_COLUMNS)\
                .select_from(MappedService)\
                .add_column(MappedService.id.label("id"))\
                .add_column(MappedService.probe_service_id.label("probe_service_id"))\
                .join(Service, MappedService.probe_service_id == Service.id)\
                .join(Status, MappedService.status_id == Status.id)\
                .outerjoin(ErrorCause, MappedService.error_cause_id == ErrorCause.id)\
                .filter((Service.probe_id == probe.id))\
                .order_by(MappedService.name)

            if allowed_statuses:
                mapped_services = mapped_services.filter(
                    MappedService.status_id.in_(allowed_statuses))

            ids = request.args.getlist("id")
            if ids:
                mapped_services = mapped_services.filter(
                    MappedService.id.in_(ids))

            services = []
            service_ids = set()
            mapped_ids = set()

            for row in mapped_services.all():
                service = row._asdict()
                services.append(service)
                service_ids.add(service["probe_service_id"])
                mapped_ids.add(service["id"])

            if show_options:
                load_value = False

                if "value" in show_options:
                    load_value = True
                    show_options.remove("value")

                # Select all options for given services
                options = select(session, show_options, self.OPTIONS_COLUMNS)\
                    .add_column(ServiceOption.probe_service_id)\
                    .add_column(ServiceOption.id)\
                    .filter(ServiceOption.probe_service_id.in_(service_ids))

                options_for_service = {}

                for row in options.all():
                    option = row._asdict()
                    options_for_service.setdefault(option["probe_service_id"],
                                                   []).append(option)

                values_by_mapping = {}

                if load_value:
                    for row in session.query(MappedServiceOption.value, MappedServiceOption.mapped_service_id, MappedServiceOption.option_id)\
                            .filter(MappedServiceOption.mapped_service_id.in_(mapped_ids)).all():
                        value = row._asdict()

                        values_by_mapping.setdefault(
                            value["mapped_service_id"],
                            {})[value["option_id"]] = value["value"]

                for service in services:
                    options = [
                        option.copy() for option in options_for_service.get(
                            service["probe_service_id"], [])
                    ]

                    if load_value:
                        for option in options:
                            option["value"] = values_by_mapping.get(
                                service["id"], {}).get(option["id"])

                    for option in options:
                        del option["probe_service_id"], option["id"]

                    service["options"] = options

            # Clean up the result struct from internal items.
            for service in services:
                del service["probe_service_id"]
                if "id" not in show:
                    del service["id"]

            return services
        finally:
            session.commit()
Ejemplo n.º 10
0
    def patch(self, probe_name):
        """
        Update mapping options.
        """
        session = config.session()

        try:
            probe = session.query(Probe).filter(Probe.name == probe_name).one()

            services = session.query(MappedService, Service)\
                .join(Service, MappedService.probe_service_id == Service.id)\
                .filter(MappedService.id.in_([service["id"] for service in request.json]))\
                .filter(Service.probe_id == probe.id)\
                .all()

            if len(services) != len(request.json):
                abort(404)

            services_by_id = {
                mapping_service[0].id: mapping_service
                for mapping_service in services
            }

            # Modify service
            for service in request.json:
                db_mapping, db_service = services_by_id[int(service["id"])]

                if "name" in service:
                    db_mapping.name = service["name"]

                if "description" in service:
                    db_mapping.description = service["description"]

                # Modify status, but only if it is not error.
                if "status" in service and db_mapping.status_id != const.status[
                        "error"]:
                    db_mapping.status_id = const.status[service["status"]]

                options_by_identifier = {}

                for option in db_service.options:
                    options_by_identifier[option.identifier] = option

                mapped_options_by_option_id = {}
                for option in db_mapping.options:
                    mapped_options_by_option_id[option.option_id] = option

                if "options" in service:
                    found_options = []
                    required_filled = True

                    for option_identifier, option_value in service[
                            "options"].items():
                        option = options_by_identifier[option_identifier]

                        # Empty options are not allowed.
                        if option_value == "":
                            if option.required:
                                required_filled = False

                            continue

                        # Test data types
                        try:
                            if option.data_type == "string":
                                pass
                            elif option.data_type == "integer":
                                option_value = str(int(option_value))
                            elif option.data_type == "double":
                                option_value = str(float(option_value))
                            elif option.data_type == "bool":
                                option_value = "1" if bool(
                                    option_value) else "0"
                            elif option.data_type == "list":
                                option_value = "\n".join([
                                    value for value in option_value.split("\n")
                                    if value.strip() != ""
                                ])
                            else:
                                raise ValueError()
                        except ValueError:
                            abort(400)

                        found_options.append(option.id)

                        if option.id in mapped_options_by_option_id:
                            mapped_options_by_option_id[
                                option.id].value = option_value

                        else:
                            mapping_option = MappedServiceOption(
                                option_id=option.id, value=option_value)
                            db_mapping.options.append(mapping_option)

                    for option_id, option in mapped_options_by_option_id.items(
                    ):
                        if option_id not in found_options:
                            session.delete(option)

                    # If all required options are filled and service is in error state because of this, reactivate the
                    # service.
                    if required_filled \
                            and db_mapping.status_id == const.status["error"] \
                            and db_mapping.error_cause_id == const.error_cause["ERROR_MISSING_REQUIRED_OPTION"]:
                        db_mapping.status_id = const.status["active"]
                        db_mapping.error_cause_id = None

                session.add(db_mapping)

            session.commit()

            return {"status": "OK"}
        except:
            session.rollback()
            raise
        finally:
            session.close()
Ejemplo n.º 11
0
    def put(self, probe_name):
        """
        Create new service mapping.
        :param probe_name: Name of probe.
        """
        data = request.json

        # List service IDs to load.
        services_to_load = [mapping["service"] for mapping in data]

        session = config.session()

        try:
            probe = session.query(Probe).filter_by(name=probe_name).one()
            services = {
                service.name: service
                for service in session.query(Service).options(
                    joinedload("options")).filter(
                        and_(Service.name.in_(services_to_load),
                             Service.probe_id == probe.id)).all()
            }

            services_by_id = {}
            for service in services.values():
                services_by_id[service.id] = service

            if len(services) != len(services_to_load):
                abort(404)

            mappings = []

            for mapping in data:
                service = services[mapping["service"]]

                db_mapping = MappedService(
                    probe_service_id=service.id,
                    name=mapping["name"],
                    description=mapping.get("description", ""),
                    status_id=const.status["active"],
                )
                db_mapping.options = []

                required_set = True

                for option in service.options:
                    option_value = mapping.get("options",
                                               {}).get(option.identifier, "")

                    if option_value == "":
                        if option.required:
                            required_set = False
                        continue

                    # Test data types
                    try:
                        if option.data_type == "string":
                            pass
                        elif option.data_type == "integer":
                            option_value = str(int(option_value))
                        elif option.data_type == "double":
                            option_value = str(float(option_value))
                        elif option.data_type == "bool":
                            option_value = "1" if bool(option_value) else "0"
                        elif option.data_type == "list":
                            option_value = "\n".join([
                                value for value in option_value.split("\n")
                                if value.strip() != ""
                            ])
                        else:
                            raise ValueError()
                    except ValueError:
                        abort(400)

                    db_mapping.options.append(
                        MappedServiceOption(option_id=option.id,
                                            value=option_value))
                    mappings.append(db_mapping)

                if not required_set:
                    db_mapping.status_id = const.status["error"]
                    db_mapping.error_cause_id = const.error_cause[
                        "ERROR_MISSING_REQUIRED_OPTION"]

            session.add_all(mappings)
            session.commit()
        except:
            session.rollback()
            raise
        finally:
            session.close()

        return {"status": "OK"}