Example #1
0
    def __init__(self):
        self.logger = api.server.app.logger
        self.devices_service = DevicesService(logger=self.logger)
        self.protocol_filter_handler = ProtocolFilterHandler(self.logger)

        self.redis_connection = redis.StrictRedis(REDIS_HOSTNAME, REDIS_PORT)
        self.redis_connection.pubsub()
Example #2
0
    def __init__(self):
        self.logger = retrieve_logger("messages_processor")

        self.walrus = Walrus(host=REDIS_HOSTNAME, port=REDIS_PORT)
        self.devices_messages = self.walrus.List('devices_messages')

        self.thread_pool_executor = ThreadPoolExecutor(
            max_workers=self.MAX_WORKERS, )

        self.devices_service = DevicesService(logger=self.logger)
        self.rules_service = RulesService(logger=self.logger)
        self.rules_executor = RulesExecutor(logger=self.logger)
        self.protocol_filter_handler = ProtocolFilterHandler(self.logger)
Example #3
0
class DevicesCommandView(MethodView):
    PATCH_REQUIRED_FIELDS = {"value"}

    def __init__(self):
        self.logger = api.server.app.logger
        self.devices_service = DevicesService(logger=self.logger)
        self.protocol_filter_handler = ProtocolFilterHandler(self.logger)

        self.redis_connection = redis.StrictRedis(REDIS_HOSTNAME, REDIS_PORT)
        self.redis_connection.pubsub()

    def _validate_patch_body(self, body):
        if not isinstance(body, dict):
            return "The body must be an object"

        missing_fields = self.PATCH_REQUIRED_FIELDS - body.keys()
        if missing_fields:
            return "Missing fields: {}".format(missing_fields)

    def patch(self, device_uuid):
        body = request.get_json()
        validation_error_message = self._validate_patch_body(body)
        if validation_error_message:
            self.logger.error(
                "Some error occurred during the validation of the body. Reason: {}"
                .format(validation_error_message))
            response = {
                "message": validation_error_message,
            }
            return jsonify(response), HTTPStatusCodes.BAD_REQUEST

        device_uuid = str(device_uuid)
        device = self.devices_service.find_by_device_id(device_uuid)
        if not device:
            self.logger.error("Device {} wasn't found".format(device_uuid))
            response = {
                "message": "Device not found",
            }
            return jsonify(response), HTTPStatusCodes.NOT_FOUND

        message = {
            "device_info": {
                "device_uuid": device["id"],
                "protocol": device["protocol"],
                "ip": device["ip"],
                "port": device.get("port", 0)
            },
            "value": body["value"],
        }
        self.protocol_filter_handler.parse(message)

        return jsonify({}), HTTPStatusCodes.NO_CONTENT
Example #4
0
    def __init__(self, device_info, address, logger):
        super().__init__(daemon=True)
        self.device_info = self._transform_sen_ml_to_json(device_info)
        self.address = address
        self.logger = logger
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

        self.devices_service = DevicesService(logger=self.logger)
        self.register_service = RegisterService(logger=self.logger)
        self.docker_service = DockerService(logger=self.logger)

        self.walrus = walrus.Database(host=GASS_REDIS_HOSTNAME,
                                      port=GASS_REDIS_PORT)
        self.containers_status = self.walrus.Hash(
            GASS_REDIS_DOCKET_CONTAINERS_PROTOCOLS_HASH_KEY)

        self.protocol_create = {
            "COAP": self._start_coap_microservice,
            "MQTT": self._start_mqtt_microservice,
        }
        self.protocol_check = {
            "COAP": self._verify_coap_microservice,
            "MQTT": self._verify_mqtt_microservices,
        }
Example #5
0
    def __init__(self):
        self.logger = retrieve_logger("message_forward")
        self.devices_service = DevicesService(logger=self.logger)

        self.redis_connection = redis_connection = redis.StrictRedis(
            REDIS_HOSTNAME, REDIS_PORT)
        self.redis_pub_sub = redis_connection.pubsub()
        self.redis_pub_sub.subscribe(MESSAGE_FORWARD_REDIS_PUBSUB)

        self.thread_pool_executor = ThreadPoolExecutor(
            max_workers=self.MAX_WORKERS)

        self.message_forward_helper = {
            "MQTT": MQTTMessageForward(),
            "COAP": CoAPMessageForward(),
        }
Example #6
0
class DevicesMessagesProcessor(object):
    MAX_WORKERS = 50

    GASS_SERVER_GATEWAY_DEVICES_PERFORMED_ACTIONS = "{}/gateways/{}/devices/actions" \
        .format(GASS_SERVER_API_URL, GATEWAY_UUID)
    GASS_SERVER_GATEWAY_DEVICES_RECEIVED_ACTIONS = "{}/gateways/{}/devices/actions/test" \
        .format(GASS_SERVER_API_URL, GATEWAY_UUID)

    def __init__(self):
        self.logger = retrieve_logger("messages_processor")

        self.walrus = Walrus(host=REDIS_HOSTNAME, port=REDIS_PORT)
        self.devices_messages = self.walrus.List('devices_messages')

        self.thread_pool_executor = ThreadPoolExecutor(
            max_workers=self.MAX_WORKERS, )

        self.devices_service = DevicesService(logger=self.logger)
        self.rules_service = RulesService(logger=self.logger)
        self.rules_executor = RulesExecutor(logger=self.logger)
        self.protocol_filter_handler = ProtocolFilterHandler(self.logger)

    def _rule_is_inside_activation_interval(self, interval_start,
                                            interval_end):
        current_date = datetime.datetime.utcnow()
        weekday_inside_interval = interval_start[
            "weekday"] <= current_date.weekday() <= interval_end["weekday"]
        if not weekday_inside_interval:
            return False

        hour_inside_interval = interval_start[
            "hour"] <= current_date.hour <= interval_end["hour"]
        if not hour_inside_interval:
            return False

        minute_inside_interval = interval_start[
            "minute"] <= current_date.minute <= interval_end["minute"]
        if not minute_inside_interval:
            return False

        return True

    def _filter_rules_by_active_interval(self, rules):
        if not rules:
            return []

        valid_rules = []
        for rule in rules:
            if not rule.get("interval"):
                valid_rules.append(rule)
                continue

            interval_start = rule["interval"]["start"]
            interval_end = rule["interval"]["end"]
            if not self._rule_is_inside_activation_interval(
                    interval_start, interval_end):
                self.logger.error(
                    "The rule {} should not be verified because at the moment rule isn't active"
                    .format(str(rule["_id"])))
                continue

            valid_rules.append(rule)

        return valid_rules

    def _send_performed_actions(self, performed_actions):
        body = performed_actions
        attempts = 0
        while True:
            self.logger.info(
                "Attempts {} to send to GaaS-Server the performed actions".
                format(attempts))
            try:
                response = requests.post(
                    self.GASS_SERVER_GATEWAY_DEVICES_PERFORMED_ACTIONS,
                    json=body)
                if response.status_code == HTTPStatusCodes.CREATED:
                    self.logger.debug(
                        "The performed actions has been sent successfully to the Gass Server"
                    )
                    return

                attempts += 1
            except Exception as err:
                self.logger.error(
                    "Failed to send the performed actions to the GaaS Server. Reason: {}"
                    .format(err), )

                self.logger.info("Sleeping...")
                time.sleep(5)
                attempts += 1
                self.logger.info(
                    "Retry sending the performed actions to the GaSS Server")

    def _test_forward_receive_actions(self, actions):
        body = actions
        attempts = 0

        while True:
            self.logger.info(
                "Attempts {} to send to GaaS-Server the received actions")
            try:
                response = requests.post(
                    self.GASS_SERVER_GATEWAY_DEVICES_RECEIVED_ACTIONS,
                    json=body)
                if response.status_code == HTTPStatusCodes.CREATED:
                    self.logger.debug(
                        "The performed actions has been sent successfully to the Gass Server"
                    )
                    return

                attempts += 1
            except Exception as err:
                self.logger.error(
                    "Failed to send the performed actions to the GaaS Server. Reason: {}"
                    .format(err),
                    exc_info=True,
                )

                self.logger.info("Sleeping...")
                time.sleep(5)
                attempts += 1
                self.logger.info(
                    "Retry sending the performed actions to the GaSS Server")

    def _send_devices_new_values(self, devices_new_values):
        devices_ids = [device['id'] for device in devices_new_values]
        devices = list(self.devices_service.find_multiple_devices(devices_ids))
        device_id_to_device_info = {device["id"]: device for device in devices}

        for device_new_value in devices_new_values:
            device_id, device_value = device_new_value["id"], device_new_value[
                "value"]
            device_info = device_id_to_device_info[device_id]
            message = {
                "device_info": {
                    "device_uuid": device_id,
                    "protocol": device_info["protocol"],
                    "ip": device_info["ip"],
                    "port": device_info.get("port", 0)
                },
                "value": device_value
            }
            self.protocol_filter_handler.parse(message)

    def _parse_message(self, message):
        try:
            self.logger.debug("Received message: {}".format(message))

            device_id, new_value = message["id"], message["v"]
            updated = self.devices_service.update(device_id, new_value)
            if not updated:
                self.logger.debug("Failed to update the sensor's value")
                return

            performed_actions = [{
                "type": ACTIONS_TYPES.CHANGE_VALUE,
                "device": device_id,
                "value": new_value,
                "timestamp": get_utc_timestamp(),
            }]
            self.logger.debug("Device has been updated")

            rules_to_check = list(
                self.rules_service.find_that_involves_devices([device_id]))
            rules_to_check = self._filter_rules_by_active_interval(
                rules_to_check)
            self.logger.debug("Have to check {} rules".format(
                len(rules_to_check)))

            devices_new_values, performed_actions_by_rules = self.rules_executor.execute(
                rules_to_check)
            performed_actions.extend(performed_actions_by_rules)
            self.logger.debug(
                "Performed actions: {}".format(performed_actions))

            self.logger.debug("Send the performed actions to the server")
            self._send_performed_actions(performed_actions)

            # Send the new values to devices to update their state
            self.logger.debug(
                "Devices new values: {}".format(devices_new_values))
            if not devices_new_values:
                return

            self._send_devices_new_values(devices_new_values)
            self.logger.debug("The new values have been sent to the devices")

        except Exception as err:
            self.logger.error(
                "Some error occurred while the message received from device was parsed. Reason: {}"
                .format(err),
                exc_info=True)

    def _write_request_latency(self, emit_time):
        now = time.time()
        latency = now - emit_time

        with open("latency.txt", mode="a") as file_handler:
            file_handler.write("{}\n".format(latency))

        self.logger.info("Current Average Latency: {}".format(latency))

    def start(self):
        self.logger.debug("Waiting for messages from devices")

        while True:
            message = self.devices_messages.bpopleft(timeout=120)
            if not message:
                continue

            message = json.loads(message.decode())
            self.logger.info("Received message: {}".format(message))
            self.thread_pool_executor.submit(self._parse_message, message)
Example #7
0
class ClientRequestHandler(threading.Thread):
    REQUIRED_FIELDS = {"id", "type", "name", "protocol", "ip"}

    MAX_ATTEMPTS_CONTAINER_PROTOCOL_START = 10

    def __init__(self, device_info, address, logger):
        super().__init__(daemon=True)
        self.device_info = self._transform_sen_ml_to_json(device_info)
        self.address = address
        self.logger = logger
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

        self.devices_service = DevicesService(logger=self.logger)
        self.register_service = RegisterService(logger=self.logger)
        self.docker_service = DockerService(logger=self.logger)

        self.walrus = walrus.Database(host=GASS_REDIS_HOSTNAME,
                                      port=GASS_REDIS_PORT)
        self.containers_status = self.walrus.Hash(
            GASS_REDIS_DOCKET_CONTAINERS_PROTOCOLS_HASH_KEY)

        self.protocol_create = {
            "COAP": self._start_coap_microservice,
            "MQTT": self._start_mqtt_microservice,
        }
        self.protocol_check = {
            "COAP": self._verify_coap_microservice,
            "MQTT": self._verify_mqtt_microservices,
        }

    def _transform_sen_ml_to_json(self, data):
        device_info = json.loads(data.decode("utf-8"))
        if device_info.get("bver"):
            device_info.pop("bver")

        name = device_info.pop("n", None)
        if name:
            device_info["name"] = name

        value = device_info.pop("v", None)
        if value:
            device_info["value"] = value

        return device_info

    def _validate_request_message(self):
        missing_fields = self.REQUIRED_FIELDS - self.device_info.keys()
        if missing_fields:
            return "The request {} has some missing fields: {}".format(
                self.device_info, missing_fields)

    def _verify_mqtt_microservices(self):
        if "MQTT" not in self.containers_status:
            return False

        if "MQTT-BROKER" not in self.containers_status:
            return False

        return True

    def _verify_coap_microservice(self):
        return "COAP" in self.containers_status

    def _start_mqtt_microservice(self):
        if "MQTT-BROKER" not in self.containers_status:
            mqtt_broker_container_id = self.docker_service.start_mqtt_broker()
            self.containers_status["MQTT-BROKER"] = mqtt_broker_container_id
            self.logger.info("Start MQTT-BROKER container with id {}".format(
                mqtt_broker_container_id))

        if "MQTT" not in self.containers_status:
            mqtt_microservice_container_id = self.docker_service.start_mqtt_microservice(
            )
            self.containers_status["MQTT"] = mqtt_microservice_container_id
            self.logger.info("Start MQTT container with id {}".format(
                mqtt_microservice_container_id))

    def _start_coap_microservice(self):
        coap_microservice_container_id = self.docker_service.start_coap_microservice(
        )
        self.containers_status["COAP"] = coap_microservice_container_id
        self.logger.info("Start COAP container with id {}".format(
            coap_microservice_container_id))

    def _verify_container(self):
        protocol = self.device_info["protocol"]
        microservices_already_up = self.protocol_check[protocol]()
        if microservices_already_up:
            self.logger.info(
                "There is a container for protocol: {}".format(protocol))
            return

        attempt = 0
        while attempt < self.MAX_ATTEMPTS_CONTAINER_PROTOCOL_START:

            try:
                self.logger.info(
                    "No docker for protocol: {}. Starting the microservice".
                    format(protocol))
                self.logger.info("Attempt : {}".format(attempt))
                self.protocol_create[protocol]()

                break

            except Exception as err:
                self.logger.error(
                    "Some error occurred while the start of the microservice for protocol {} . Reason: {}"
                    .format(protocol, err),
                    exc_info=True)
                attempt += 1

    def run(self):
        self.logger.debug("Received register response: {} ---- from {}".format(
            self.device_info, self.address))
        error_validation_message = self._validate_request_message()
        if error_validation_message:
            self.logger.error(error_validation_message)
            self.sock.sendto(b"MISSING FIELDS", self.address)
            self.sock.close()
            return

        self.device_info["gateway_uuid"] = GATEWAY_UUID
        devices_has_been_register = self.register_service.register_device(
            self.device_info)
        if not devices_has_been_register:
            self.logger.critical(
                "Failed to register the device to GaSS Server")
            self.sock.sendto(
                b"Failed to register because the GaSS server is unreachable",
                self.address)
            self.sock.close()
            return

        device = self.devices_service.find_by_device_id(self.device_info["id"])
        if device:
            self.logger.debug("Device {} has been already registered".format(
                self.device_info["id"]))

            self._verify_container()

            self.sock.sendto(b"OK", self.address)
            self.sock.close()
            return

        device = self.devices_service.create(dict(self.device_info))
        if not device:
            self.logger.error("Failed to create the device")
            self.sock.sendto(b"FAILED TO REGISTER", self.address)
            self.sock.close()
            return

        # Start the micro-service
        # Check in microservice is on or not
        # If it will create the microservice, store the container id into REDIS
        # So, the monitor will be able to check which container is still alive
        # And if it finds that some containers doesn't exists will create another one

        self._verify_container()

        try:
            self.logger.debug("Send ACK to device at address: {}: {}".format(
                self.address[0], self.address[1]))
        except Exception as err:
            print(err)
            raise
        self.sock.sendto(b"OK", self.address)
        self.sock.close()

        self.logger.debug("Devices has been registered")
        self.logger.debug("Sent registering confirmation")
    def __init__(self, *args, **kwars):
        self.logger = kwars.get("logger") or FakeLogger()

        self.rules_engine = RulesEngine(logger=self.logger)
        self.devices_service = DevicesService(logger=self.logger)
class RulesExecutor(object):
    def __init__(self, *args, **kwars):
        self.logger = kwars.get("logger") or FakeLogger()

        self.rules_engine = RulesEngine(logger=self.logger)
        self.devices_service = DevicesService(logger=self.logger)

    def execute(self, rules):
        if not rules:
            return [], []

        performed_actions = []
        devices_new_values = []
        rules_to_check = list(rules)
        for rule in rules_to_check:
            rule_id = str(rule["_id"])
            self.logger.debug("Process rule {}".format(rule_id))

            devices = self.devices_service.find_multiple_devices(
                rule["devices_involved"])
            device_id_to_value = {
                device["id"]: device["value"]
                for device in devices
            }
            missing_devices = set(
                rule["devices_involved"]) - device_id_to_value.keys()
            if missing_devices:
                self.logger.error(
                    "Some devices are missing {}".format(missing_devices))
                continue

            rule_was_triggered = self.rules_engine.evaluate_rule(
                rule, device_id_to_value)
            if not rule_was_triggered:
                self.logger.debug(
                    "Rule {} has been evaluated and wasn't triggered".format(
                        rule_id))
                continue

            performed_actions.append({
                "type": ACTIONS_TYPES.RULE_TRIGGERED,
                "rule": rule_id,
                "timestamp": get_utc_timestamp()
            })
            self.logger.debug(
                "Rule {} has been evaluated and it was triggered. Perform the in cause actions"
                .format(rule_id))
            actions_to_do = rule["actions"]
            if not actions_to_do:
                self.logger.debug(
                    "No actions has to be performed for rule {}".format(
                        rule_id))
                continue

            for device_id, new_value in actions_to_do.items():
                devices_new_values.append({
                    "id": device_id,
                    "value": new_value
                })

        return devices_new_values, performed_actions