예제 #1
0
 def test_completes_op_with_error(self, mocker, stage, op, fake_exception,
                                  callback):
     op.error = fake_exception
     op.callback = callback
     pass_op_to_next_stage(stage, op)
     assert_callback_failed(op=op, error=fake_exception)
     assert stage.next.run_op.call_count == 0
예제 #2
0
 def _execute_op(self, op):
     if isinstance(op, pipeline_ops_iothub.SetAuthProviderOperation):
         self.auth_provider = op.auth_provider
         self.auth_provider.on_sas_token_updated_handler = self.on_sas_token_updated
         operation_flow.delegate_to_different_op(
             stage=self,
             original_op=op,
             new_op=pipeline_ops_iothub.SetIoTHubConnectionArgsOperation(
                 device_id=self.auth_provider.device_id,
                 module_id=getattr(self.auth_provider, "module_id", None),
                 hostname=self.auth_provider.hostname,
                 gateway_hostname=getattr(self.auth_provider,
                                          "gateway_hostname", None),
                 ca_cert=getattr(self.auth_provider, "ca_cert", None),
                 sas_token=self.auth_provider.get_current_sas_token(),
             ),
         )
     elif isinstance(op, pipeline_ops_iothub.SetX509AuthProviderOperation):
         self.auth_provider = op.auth_provider
         operation_flow.delegate_to_different_op(
             stage=self,
             original_op=op,
             new_op=pipeline_ops_iothub.SetIoTHubConnectionArgsOperation(
                 device_id=self.auth_provider.device_id,
                 module_id=getattr(self.auth_provider, "module_id", None),
                 hostname=self.auth_provider.hostname,
                 gateway_hostname=getattr(self.auth_provider,
                                          "gateway_hostname", None),
                 ca_cert=getattr(self.auth_provider, "ca_cert", None),
                 client_cert=self.auth_provider.get_x509_certificate(),
             ),
         )
     else:
         operation_flow.pass_op_to_next_stage(self, op)
    def _execute_op(self, op):
        if isinstance(op, pipeline_ops_provisioning.SetSymmetricKeySecurityClientOperation):

            security_client = op.security_client
            operation_flow.delegate_to_different_op(
                stage=self,
                original_op=op,
                new_op=pipeline_ops_provisioning.SetProvisioningClientConnectionArgsOperation(
                    provisioning_host=security_client.provisioning_host,
                    registration_id=security_client.registration_id,
                    id_scope=security_client.id_scope,
                    sas_token=security_client.get_current_sas_token(),
                ),
            )

        elif isinstance(op, pipeline_ops_provisioning.SetX509SecurityClientOperation):
            security_client = op.security_client
            operation_flow.delegate_to_different_op(
                stage=self,
                original_op=op,
                new_op=pipeline_ops_provisioning.SetProvisioningClientConnectionArgsOperation(
                    provisioning_host=security_client.provisioning_host,
                    registration_id=security_client.registration_id,
                    id_scope=security_client.id_scope,
                    client_cert=security_client.get_x509_certificate(),
                ),
            )

        else:
            operation_flow.pass_op_to_next_stage(self, op)
예제 #4
0
    def on_sas_token_updated(self):
        logger.info(
            "{}: New sas token received.  Passing down UpdateSasTokenOperation."
            .format(self.name))

        @pipeline_thread.runs_on_pipeline_thread
        def on_token_update_complete(op):
            if op.error:
                logger.error(
                    "{}({}): token update operation failed.  Error={}".format(
                        self.name, op.name, op.error))
                unhandled_exceptions.exception_caught_in_background_thread(
                    op.error)
            else:
                logger.debug(
                    "{}({}): token update operation is complete".format(
                        self.name, op.name))

        operation_flow.pass_op_to_next_stage(
            stage=self,
            op=pipeline_ops_base.UpdateSasTokenOperation(
                sas_token=self.auth_provider.get_current_sas_token(),
                callback=on_token_update_complete,
            ),
        )
예제 #5
0
            def on_token_update_complete(op):
                op.callback = old_callback
                if op.error:
                    logger.error(
                        "{}({}) token update failed.  returning failure {}".
                        format(self.name, op.name, op.error))
                    operation_flow.complete_op(stage=self, op=op)
                else:
                    logger.debug(
                        "{}({}) token update succeeded.  reconnecting".format(
                            self.name, op.name))

                    operation_flow.pass_op_to_next_stage(
                        stage=self,
                        op=pipeline_ops_base.ReconnectOperation(
                            callback=on_reconnect_complete),
                    )

                logger.debug(
                    "{}({}): passing to next stage with updated callback.".
                    format(self.name, op.name))
    def _execute_op(self, op):
        def map_twin_error(original_op, twin_op):
            if twin_op.error:
                original_op.error = twin_op.error
            elif twin_op.status_code >= 300:
                # TODO map error codes to correct exceptions
                logger.error("Error {} received from twin operation".format(
                    twin_op.status_code))
                logger.error("response body: {}".format(twin_op.response_body))
                original_op.error = Exception(
                    "twin operation returned status {}".format(
                        twin_op.status_code))

        if isinstance(op, pipeline_ops_iothub.GetTwinOperation):

            def on_twin_response(twin_op):
                logger.info("{}({}): Got response for GetTwinOperation".format(
                    self.name, op.name))
                map_twin_error(original_op=op, twin_op=twin_op)
                if not twin_op.error:
                    op.twin = json.loads(twin_op.response_body.decode("utf-8"))
                operation_flow.complete_op(self, op)

            operation_flow.pass_op_to_next_stage(
                self,
                pipeline_ops_base.SendIotRequestAndWaitForResponseOperation(
                    request_type=constant.TWIN,
                    method="GET",
                    resource_location="/",
                    request_body=" ",
                    callback=on_twin_response,
                ),
            )

        elif isinstance(
                op, pipeline_ops_iothub.PatchTwinReportedPropertiesOperation):

            def on_twin_response(twin_op):
                logger.info(
                    "{}({}): Got response for PatchTwinReportedPropertiesOperation operation"
                    .format(self.name, op.name))
                map_twin_error(original_op=op, twin_op=twin_op)
                operation_flow.complete_op(self, op)

            logger.info("{}({}): Sending reported properties patch: {}".format(
                self.name, op.name, op.patch))

            operation_flow.pass_op_to_next_stage(
                self,
                (pipeline_ops_base.SendIotRequestAndWaitForResponseOperation(
                    request_type=constant.TWIN,
                    method="PATCH",
                    resource_location="/properties/reported/",
                    request_body=json.dumps(op.patch),
                    callback=on_twin_response,
                )),
            )

        else:
            operation_flow.pass_op_to_next_stage(self, op)
예제 #7
0
    def _run_op(self, op):
        def pipeline_ops_done(completed_op):
            op.error = completed_op.error
            op.callback(op)

        if isinstance(op, pipeline_ops_iothub.SetAuthProviderOperation):
            auth_provider = op.auth_provider
            operation_flow.run_ops_in_serial(
                self,
                pipeline_ops_iothub.SetAuthProviderArgsOperation(
                    device_id=auth_provider.device_id,
                    module_id=getattr(auth_provider, "module_id", None),
                    hostname=auth_provider.hostname,
                    gateway_hostname=getattr(auth_provider, "gateway_hostname", None),
                    ca_cert=getattr(auth_provider, "ca_cert", None),
                ),
                pipeline_ops_base.SetSasTokenOperation(
                    sas_token=auth_provider.get_current_sas_token()
                ),
                callback=pipeline_ops_done,
            )
        elif isinstance(op, pipeline_ops_iothub.SetX509AuthProviderOperation):
            auth_provider = op.auth_provider
            operation_flow.run_ops_in_serial(
                self,
                pipeline_ops_iothub.SetAuthProviderArgsOperation(
                    device_id=auth_provider.device_id,
                    module_id=getattr(auth_provider, "module_id", None),
                    hostname=auth_provider.hostname,
                    gateway_hostname=getattr(auth_provider, "gateway_hostname", None),
                    ca_cert=getattr(auth_provider, "ca_cert", None),
                ),
                pipeline_ops_base.SetClientAuthenticationCertificateOperation(
                    certificate=auth_provider.get_x509_certificate()
                ),
                callback=pipeline_ops_done,
            )
        else:
            operation_flow.pass_op_to_next_stage(self, op)
예제 #8
0
    def _run_op(self, op):
        def pipeline_ops_done(completed_op):
            op.error = completed_op.error
            op.callback(op)

        if isinstance(
                op, pipeline_ops_provisioning.
                SetSymmetricKeySecurityClientOperation):

            security_client = op.security_client
            operation_flow.run_ops_in_serial(
                self,
                pipeline_ops_provisioning.SetSecurityClientArgsOperation(
                    provisioning_host=security_client.provisioning_host,
                    registration_id=security_client.registration_id,
                    id_scope=security_client.id_scope,
                ),
                pipeline_ops_base.SetSasTokenOperation(
                    sas_token=security_client.get_current_sas_token()),
                callback=pipeline_ops_done,
            )
        elif isinstance(
                op, pipeline_ops_provisioning.SetX509SecurityClientOperation):
            security_client = op.security_client
            operation_flow.run_ops_in_serial(
                self,
                pipeline_ops_provisioning.SetSecurityClientArgsOperation(
                    provisioning_host=security_client.provisioning_host,
                    registration_id=security_client.registration_id,
                    id_scope=security_client.id_scope,
                ),
                pipeline_ops_base.SetClientAuthenticationCertificateOperation(
                    certificate=security_client.get_x509_certificate()),
                callback=pipeline_ops_done,
            )

        else:
            operation_flow.pass_op_to_next_stage(self, op)
 def _run_op(self, op):
     operation_flow.pass_op_to_next_stage(self, op)
예제 #10
0
 def test_fails_op_when_no_next_stage(self, stage, op, callback):
     op.callback = callback
     stage.next = None
     pass_op_to_next_stage(stage, op)
     assert_callback_failed(op=op)
     pass
예제 #11
0
 def test_passes_op_to_next_stage(self, mocker, stage, op, callback):
     pass_op_to_next_stage(stage, op)
     assert stage.next.run_op.call_count == 1
     assert stage.next.run_op.call_args == mocker.call(op)
예제 #12
0
    def _execute_op(self, op):

        if isinstance(op,
                      pipeline_ops_iothub.SetIoTHubConnectionArgsOperation):
            self.device_id = op.device_id
            self.module_id = op.module_id

            # if we get auth provider args from above, we save some, use some to build topic names,
            # and always pass it down because we know that the MQTT protocol stage will also want
            # to receive these args.
            self._set_topic_names(device_id=op.device_id,
                                  module_id=op.module_id)

            if op.module_id:
                client_id = "{}/{}".format(op.device_id, op.module_id)
            else:
                client_id = op.device_id

            query_param_seq = [
                ("api-version", pkg_constant.IOTHUB_API_VERSION),
                ("DeviceClientType", pkg_constant.USER_AGENT),
            ]
            username = "******".format(
                hostname=op.hostname,
                client_id=client_id,
                query_params=urllib.parse.urlencode(query_param_seq),
            )

            if op.gateway_hostname:
                hostname = op.gateway_hostname
            else:
                hostname = op.hostname

            # TODO: test to make sure client_cert and sas_token travel down correctly
            operation_flow.delegate_to_different_op(
                stage=self,
                original_op=op,
                new_op=pipeline_ops_mqtt.SetMQTTConnectionArgsOperation(
                    client_id=client_id,
                    hostname=hostname,
                    username=username,
                    ca_cert=op.ca_cert,
                    client_cert=op.client_cert,
                    sas_token=op.sas_token,
                ),
            )

        elif (isinstance(op, pipeline_ops_base.UpdateSasTokenOperation)
              and self.pipeline_root.connected):
            logger.debug(
                "{}({}): Connected.  Passing op down and reconnecting after token is updated."
                .format(self.name, op.name))

            # make a callback that can call the user's callback after the reconnect is complete
            def on_reconnect_complete(reconnect_op):
                if reconnect_op.error:
                    op.error = reconnect_op.error
                    logger.error(
                        "{}({}) reconnection failed.  returning error {}".
                        format(self.name, op.name, op.error))
                    operation_flow.complete_op(stage=self, op=op)
                else:
                    logger.debug(
                        "{}({}) reconnection succeeded.  returning success.".
                        format(self.name, op.name))
                    operation_flow.complete_op(stage=self, op=op)

            # save the old user callback so we can call it later.
            old_callback = op.callback

            # make a callback that either fails the UpdateSasTokenOperation (if the lower level failed it),
            # or issues a ReconnectOperation (if the lower level returned success for the UpdateSasTokenOperation)
            def on_token_update_complete(op):
                op.callback = old_callback
                if op.error:
                    logger.error(
                        "{}({}) token update failed.  returning failure {}".
                        format(self.name, op.name, op.error))
                    operation_flow.complete_op(stage=self, op=op)
                else:
                    logger.debug(
                        "{}({}) token update succeeded.  reconnecting".format(
                            self.name, op.name))

                    operation_flow.pass_op_to_next_stage(
                        stage=self,
                        op=pipeline_ops_base.ReconnectOperation(
                            callback=on_reconnect_complete),
                    )

                logger.debug(
                    "{}({}): passing to next stage with updated callback.".
                    format(self.name, op.name))

            # now, pass the UpdateSasTokenOperation down with our new callback.
            op.callback = on_token_update_complete
            operation_flow.pass_op_to_next_stage(stage=self, op=op)

        elif isinstance(
                op, pipeline_ops_iothub.SendD2CMessageOperation) or isinstance(
                    op, pipeline_ops_iothub.SendOutputEventOperation):
            # Convert SendTelementry and SendOutputEventOperation operations into MQTT Publish operations
            topic = mqtt_topic_iothub.encode_properties(
                op.message, self.telemetry_topic)
            operation_flow.delegate_to_different_op(
                stage=self,
                original_op=op,
                new_op=pipeline_ops_mqtt.MQTTPublishOperation(
                    topic=topic, payload=op.message.data),
            )

        elif isinstance(op, pipeline_ops_iothub.SendMethodResponseOperation):
            # Sending a Method Response gets translated into an MQTT Publish operation
            topic = mqtt_topic_iothub.get_method_topic_for_publish(
                op.method_response.request_id, str(op.method_response.status))
            payload = json.dumps(op.method_response.payload)
            operation_flow.delegate_to_different_op(
                stage=self,
                original_op=op,
                new_op=pipeline_ops_mqtt.MQTTPublishOperation(topic=topic,
                                                              payload=payload),
            )

        elif isinstance(op, pipeline_ops_base.EnableFeatureOperation):
            # Enabling a feature gets translated into an MQTT subscribe operation
            topic = self.feature_to_topic[op.feature_name]
            operation_flow.delegate_to_different_op(
                stage=self,
                original_op=op,
                new_op=pipeline_ops_mqtt.MQTTSubscribeOperation(topic=topic),
            )

        elif isinstance(op, pipeline_ops_base.DisableFeatureOperation):
            # Disabling a feature gets turned into an MQTT unsubscribe operation
            topic = self.feature_to_topic[op.feature_name]
            operation_flow.delegate_to_different_op(
                stage=self,
                original_op=op,
                new_op=pipeline_ops_mqtt.MQTTUnsubscribeOperation(topic=topic),
            )

        elif isinstance(op, pipeline_ops_base.SendIotRequestOperation):
            if op.request_type == pipeline_constant.TWIN:
                topic = mqtt_topic_iothub.get_twin_topic_for_publish(
                    method=op.method,
                    resource_location=op.resource_location,
                    request_id=op.request_id,
                )
                operation_flow.delegate_to_different_op(
                    stage=self,
                    original_op=op,
                    new_op=pipeline_ops_mqtt.MQTTPublishOperation(
                        topic=topic, payload=op.request_body),
                )
            else:
                raise NotImplementedError(
                    "SendIotRequestOperation request_type {} not supported".
                    format(op.request_type))

        else:
            # All other operations get passed down
            operation_flow.pass_op_to_next_stage(self, op)
예제 #13
0
 def _execute_op(self, op):
     operation_flow.pass_op_to_next_stage(self, op)
예제 #14
0
    def _run_op(self, op):

        if isinstance(op, pipeline_ops_iothub.SetAuthProviderArgsOperation):
            self.device_id = op.device_id
            self.module_id = op.module_id

            # if we get auth provider args from above, we save some, use some to build topic names,
            # and always pass it down because we know that the MQTT protocol stage will also want
            # to receive these args.
            self._set_topic_names(device_id=op.device_id,
                                  module_id=op.module_id)

            if op.module_id:
                client_id = "{}/{}".format(op.device_id, op.module_id)
            else:
                client_id = op.device_id

            username = "******".format(
                hostname=op.hostname, client_id=client_id)

            if op.gateway_hostname:
                hostname = op.gateway_hostname
            else:
                hostname = op.hostname

            operation_flow.delegate_to_different_op(
                stage=self,
                original_op=op,
                new_op=pipeline_ops_mqtt.SetMQTTConnectionArgsOperation(
                    client_id=client_id,
                    hostname=hostname,
                    username=username,
                    ca_cert=op.ca_cert),
            )

        elif isinstance(
                op, pipeline_ops_iothub.SendD2CMessageOperation) or isinstance(
                    op, pipeline_ops_iothub.SendOutputEventOperation):
            # Convert SendTelementry and SendOutputEventOperation operations into MQTT Publish operations
            topic = mqtt_topic_iothub.encode_properties(
                op.message, self.telemetry_topic)
            operation_flow.delegate_to_different_op(
                stage=self,
                original_op=op,
                new_op=pipeline_ops_mqtt.MQTTPublishOperation(
                    topic=topic, payload=op.message.data),
            )

        elif isinstance(op, pipeline_ops_iothub.SendMethodResponseOperation):
            # Sending a Method Response gets translated into an MQTT Publish operation
            topic = mqtt_topic_iothub.get_method_topic_for_publish(
                op.method_response.request_id, str(op.method_response.status))
            payload = json.dumps(op.method_response.payload)
            operation_flow.delegate_to_different_op(
                stage=self,
                original_op=op,
                new_op=pipeline_ops_mqtt.MQTTPublishOperation(topic=topic,
                                                              payload=payload),
            )

        elif isinstance(op, pipeline_ops_base.EnableFeatureOperation):
            # Enabling a feature gets translated into an MQTT subscribe operation
            topic = self.feature_to_topic[op.feature_name]
            operation_flow.delegate_to_different_op(
                stage=self,
                original_op=op,
                new_op=pipeline_ops_mqtt.MQTTSubscribeOperation(topic=topic),
            )

        elif isinstance(op, pipeline_ops_base.DisableFeatureOperation):
            # Disabling a feature gets turned into an MQTT unsubscribe operation
            topic = self.feature_to_topic[op.feature_name]
            operation_flow.delegate_to_different_op(
                stage=self,
                original_op=op,
                new_op=pipeline_ops_mqtt.MQTTUnsubscribeOperation(topic=topic),
            )

        elif isinstance(op, pipeline_ops_base.SendIotRequestOperation):
            if op.request_type == constant.TWIN:
                topic = mqtt_topic_iothub.get_twin_topic_for_publish(
                    method=op.method,
                    resource_location=op.resource_location,
                    request_id=op.request_id,
                )
                operation_flow.delegate_to_different_op(
                    stage=self,
                    original_op=op,
                    new_op=pipeline_ops_mqtt.MQTTPublishOperation(
                        topic=topic, payload=op.request_body),
                )
            else:
                raise NotImplementedError(
                    "SendIotRequestOperation request_type {} not supported".
                    format(op.request_type))

        else:
            # All other operations get passed down
            operation_flow.pass_op_to_next_stage(self, op)
예제 #15
0
 def _execute_op(self, op):
     pass_op_to_next_stage(self, op)
예제 #16
0
    def _execute_op(self, op):

        if isinstance(
                op, pipeline_ops_provisioning.
                SetProvisioningClientConnectionArgsOperation):
            # get security client args from above, save some, use some to build topic names,
            # always pass it down because MQTT protocol stage will also want to receive these args.

            client_id = op.registration_id
            query_param_seq = [
                ("api-version", pkg_constant.PROVISIONING_API_VERSION),
                ("ClientVersion", pkg_constant.USER_AGENT),
            ]
            username = "******".format(
                id_scope=op.id_scope,
                registration_id=op.registration_id,
                query_params=urllib.parse.urlencode(query_param_seq),
            )

            hostname = op.provisioning_host

            operation_flow.delegate_to_different_op(
                stage=self,
                original_op=op,
                new_op=pipeline_ops_mqtt.SetMQTTConnectionArgsOperation(
                    client_id=client_id,
                    hostname=hostname,
                    username=username,
                    client_cert=op.client_cert,
                    sas_token=op.sas_token,
                ),
            )

        elif isinstance(
                op,
                pipeline_ops_provisioning.SendRegistrationRequestOperation):
            # Convert Sending the request into MQTT Publish operations
            topic = mqtt_topic.get_topic_for_register(op.request_id)
            operation_flow.delegate_to_different_op(
                stage=self,
                original_op=op,
                new_op=pipeline_ops_mqtt.MQTTPublishOperation(
                    topic=topic, payload=op.request_payload),
            )

        elif isinstance(op,
                        pipeline_ops_provisioning.SendQueryRequestOperation):
            # Convert Sending the request into MQTT Publish operations
            topic = mqtt_topic.get_topic_for_query(op.request_id,
                                                   op.operation_id)
            operation_flow.delegate_to_different_op(
                stage=self,
                original_op=op,
                new_op=pipeline_ops_mqtt.MQTTPublishOperation(
                    topic=topic, payload=op.request_payload),
            )

        elif isinstance(op, pipeline_ops_base.EnableFeatureOperation):
            # Enabling for register gets translated into an MQTT subscribe operation
            topic = mqtt_topic.get_topic_for_subscribe()
            operation_flow.delegate_to_different_op(
                stage=self,
                original_op=op,
                new_op=pipeline_ops_mqtt.MQTTSubscribeOperation(topic=topic),
            )

        elif isinstance(op, pipeline_ops_base.DisableFeatureOperation):
            # Disabling a register response gets turned into an MQTT unsubscribe operation
            topic = mqtt_topic.get_topic_for_subscribe()
            operation_flow.delegate_to_different_op(
                stage=self,
                original_op=op,
                new_op=pipeline_ops_mqtt.MQTTUnsubscribeOperation(topic=topic),
            )

        else:
            # All other operations get passed down
            operation_flow.pass_op_to_next_stage(self, op)
예제 #17
0
 def _run_op(self, op):
     pass_op_to_next_stage(self, op)