def __init__(self, pipeline_configuration): """ Constructor for instantiating a pipeline adapter object. :param auth_provider: The authentication provider :param pipeline_configuration: The configuration generated based on user inputs """ # NOTE: This pipeline DOES NOT handle SasToken management! # (i.e. using a SasTokenStage) # It instead relies on the parallel MQTT pipeline to handle that. # # Because they share a pipeline configuration, and MQTT has renewal logic we can be sure # that the SasToken in the pipeline configuration is valid. # # Furthermore, because HTTP doesn't require constant connections or long running tokens, # there's no need to reauthorize connections, so we can just pass the token from the config # when needed for auth. # # This is not an ideal solution, but it's the simplest one for the time being. # Contains data and information shared globally within the pipeline self._nucleus = pipeline_nucleus.PipelineNucleus( pipeline_configuration) self._pipeline = (pipeline_stages_base.PipelineRootStage( self._nucleus).append_stage( pipeline_stages_iothub_http.IoTHubHTTPTranslationStage( )).append_stage(pipeline_stages_http.HTTPTransportStage())) callback = EventedCallback() op = pipeline_ops_base.InitializePipelineOperation(callback=callback) self._pipeline.run_op(op) callback.wait_for_completion()
def stage(self, mocker, request, cls_type, init_kwargs, mock_transport): stage = cls_type(**init_kwargs) stage.pipeline_root = pipeline_stages_base.PipelineRootStage( pipeline_configuration=mocker.MagicMock()) stage.send_op_down = mocker.MagicMock() # Set up the Transport on the stage op = pipeline_ops_base.InitializePipelineOperation( callback=mocker.MagicMock()) stage.run_op(op) assert stage.transport is mock_transport.return_value return stage
def stage(self, mocker, request, cls_type, init_kwargs, mock_transport): stage = cls_type(**init_kwargs) stage.nucleus = pipeline_nucleus.PipelineNucleus( pipeline_configuration=mocker.MagicMock()) stage.send_op_down = mocker.MagicMock() stage.send_event_up = mocker.MagicMock() mocker.spy(stage, "report_background_exception") # Set up the Transport on the stage op = pipeline_ops_base.InitializePipelineOperation( callback=mocker.MagicMock()) stage.run_op(op) assert stage.transport is mock_transport.return_value return stage
def __init__(self, pipeline_configuration): """ Constructor for instantiating a pipeline adapter object. :param auth_provider: The authentication provider :param pipeline_configuration: The configuration generated based on user inputs """ self._pipeline = (pipeline_stages_base.PipelineRootStage( pipeline_configuration).append_stage( pipeline_stages_base.SasTokenRenewalStage()).append_stage( pipeline_stages_iothub_http.IoTHubHTTPTranslationStage( )).append_stage(pipeline_stages_http.HTTPTransportStage())) callback = EventedCallback() op = pipeline_ops_base.InitializePipelineOperation(callback=callback) self._pipeline.run_op(op) callback.wait_for_completion()
def __init__(self, pipeline_configuration): """ Constructor for instantiating a pipeline :param security_client: The security client which stores credentials """ self.responses_enabled = {provisioning_constants.REGISTER: False} # Event Handlers - Will be set by Client after instantiation of pipeline self.on_connected = None self.on_disconnected = None self.on_message_received = None self._registration_id = pipeline_configuration.registration_id self._pipeline = ( # # The root is always the root. By definition, it's the first stage in the pipeline. # pipeline_stages_base.PipelineRootStage( pipeline_configuration=pipeline_configuration) # # SasTokenRenewalStage comes near the root by default because it should be as close # to the top of the pipeline as possible, and does not need to be after anything. # .append_stage(pipeline_stages_base.SasTokenRenewalStage()) # # RegistrationStage needs to come early because this is the stage that converts registration # or query requests into request and response objects which are used by later stages # .append_stage(pipeline_stages_provisioning.RegistrationStage()) # # PollingStatusStage needs to come after RegistrationStage because RegistrationStage counts # on PollingStatusStage to poll until the registration is complete. # .append_stage(pipeline_stages_provisioning.PollingStatusStage()) # # CoordinateRequestAndResponseStage needs to be after RegistrationStage and PollingStatusStage # because these 2 stages create the request ops that CoordinateRequestAndResponseStage # is coordinating. It needs to be before ProvisioningMQTTTranslationStage because that stage # operates on ops that CoordinateRequestAndResponseStage produces # .append_stage( pipeline_stages_base.CoordinateRequestAndResponseStage()) # # ProvisioningMQTTTranslationStage comes here because this is the point where we can translate # all operations directly into MQTT. After this stage, only pipeline_stages_base stages # are allowed because ProvisioningMQTTTranslationStage removes all the provisioning-ness from the ops # .append_stage(pipeline_stages_provisioning_mqtt. ProvisioningMQTTTranslationStage()) # # AutoConnectStage comes here because only MQTT ops have the need_connection flag set # and this is the first place in the pipeline wherer we can guaranetee that all network # ops are MQTT ops. # .append_stage(pipeline_stages_base.AutoConnectStage()) # # ReconnectStage needs to be after AutoConnectStage because ReconnectStage sets/clears # the virtually_conencted flag and we want an automatic connection op to set this flag so # we can reconnect autoconnect operations. # .append_stage(pipeline_stages_base.ReconnectStage()) # # ConnectionLockStage needs to be after ReconnectStage because we want any ops that # ReconnectStage creates to go through the ConnectionLockStage gate # .append_stage(pipeline_stages_base.ConnectionLockStage()) # # RetryStage needs to be near the end because it's retrying low-level MQTT operations. # .append_stage(pipeline_stages_base.RetryStage()) # # OpTimeoutStage needs to be after RetryStage because OpTimeoutStage returns the timeout # errors that RetryStage is watching for. # .append_stage(pipeline_stages_base.OpTimeoutStage()) # # MQTTTransportStage needs to be at the very end of the pipeline because this is where # operations turn into network traffic # .append_stage(pipeline_stages_mqtt.MQTTTransportStage())) def _on_pipeline_event(event): logger.warning("Dropping unknown pipeline event {}".format( event.name)) def _on_connected(): if self.on_connected: self.on_connected("connected") def _on_disconnected(): if self.on_disconnected: self.on_disconnected("disconnected") self._pipeline.on_pipeline_event_handler = _on_pipeline_event self._pipeline.on_connected_handler = _on_connected self._pipeline.on_disconnected_handler = _on_disconnected callback = EventedCallback() op = pipeline_ops_base.InitializePipelineOperation(callback=callback) self._pipeline.run_op(op) callback.wait_for_completion()
def op(self, mocker): return pipeline_ops_base.InitializePipelineOperation( callback=mocker.MagicMock())
def __init__(self, pipeline_configuration): """ Constructor for instantiating a pipeline adapter object :param auth_provider: The authentication provider :param pipeline_configuration: The configuration generated based on user inputs """ self.feature_enabled = { constant.C2D_MSG: False, constant.INPUT_MSG: False, constant.METHODS: False, constant.TWIN: False, constant.TWIN_PATCHES: False, } # Handlers - Will be set by Client after instantiation of this object self.on_connected = None self.on_disconnected = None self.on_new_sastoken_required = None self.on_background_exception = None self.on_c2d_message_received = None self.on_input_message_received = None self.on_method_request_received = None self.on_twin_patch_received = None # Contains data and information shared globally within the pipeline self._nucleus = pipeline_nucleus.PipelineNucleus(pipeline_configuration) self._pipeline = ( # # The root is always the root. By definition, it's the first stage in the pipeline. # pipeline_stages_base.PipelineRootStage(self._nucleus) # # SasTokenStage comes near the root by default because it should be as close # to the top of the pipeline as possible, and does not need to be after anything. # .append_stage(pipeline_stages_base.SasTokenStage()) # # EnsureDesiredPropertiesStage needs to be above TwinRequestResponseStage because it # sends GetTwinOperation ops and that stage handles those ops. # .append_stage(pipeline_stages_iothub.EnsureDesiredPropertiesStage()) # # TwinRequestResponseStage comes near the root by default because it doesn't need to be # after anything # .append_stage(pipeline_stages_iothub.TwinRequestResponseStage()) # # CoordinateRequestAndResponseStage needs to be after TwinRequestResponseStage because # TwinRequestResponseStage creates the request ops that CoordinateRequestAndResponseStage # is coordinating. It needs to be before IoTHubMQTTTranslationStage because that stage # operates on ops that CoordinateRequestAndResponseStage produces # .append_stage(pipeline_stages_base.CoordinateRequestAndResponseStage()) # # IoTHubMQTTTranslationStage comes here because this is the point where we can translate # all operations directly into MQTT. After this stage, only pipeline_stages_base stages # are allowed because IoTHubMQTTTranslationStage removes all the IoTHub-ness from the ops # .append_stage(pipeline_stages_iothub_mqtt.IoTHubMQTTTranslationStage()) # # AutoConnectStage comes here because only MQTT ops have the need_connection flag set # and this is the first place in the pipeline where we can guarantee that all network # ops are MQTT ops. # .append_stage(pipeline_stages_base.AutoConnectStage()) # # ConnectionStateStage needs to be after AutoConnectStage because the AutoConnectStage # can create ConnectOperations and we (may) want to queue connection related operations # in the ConnectionStateStage # .append_stage(pipeline_stages_base.ConnectionStateStage()) # # ConnectionLockStage needs to be after ConnectionStateStage because we want any ops that # ConnectionStateStage creates to go through the ConnectionLockStage gate # .append_stage(pipeline_stages_base.ConnectionLockStage()) # # RetryStage needs to be near the end because it's retrying low-level MQTT operations. # .append_stage(pipeline_stages_base.RetryStage()) # # OpTimeoutStage needs to be after RetryStage because OpTimeoutStage returns the timeout # errors that RetryStage is watching for. # .append_stage(pipeline_stages_base.OpTimeoutStage()) # # MQTTTransportStage needs to be at the very end of the pipeline because this is where # operations turn into network traffic # .append_stage(pipeline_stages_mqtt.MQTTTransportStage()) ) # Define behavior for domain-specific events def _on_pipeline_event(event): if isinstance(event, pipeline_events_iothub.C2DMessageEvent): if self.on_c2d_message_received: self.on_c2d_message_received(event.message) else: logger.error("C2D message event received with no handler. dropping.") elif isinstance(event, pipeline_events_iothub.InputMessageEvent): if self.on_input_message_received: self.on_input_message_received(event.message) else: logger.error("input message event received with no handler. dropping.") elif isinstance(event, pipeline_events_iothub.MethodRequestEvent): if self.on_method_request_received: self.on_method_request_received(event.method_request) else: logger.error("Method request event received with no handler. Dropping.") elif isinstance(event, pipeline_events_iothub.TwinDesiredPropertiesPatchEvent): if self.on_twin_patch_received: self.on_twin_patch_received(event.patch) else: logger.error("Twin patch event received with no handler. Dropping.") else: logger.error("Dropping unknown pipeline event {}".format(event.name)) def _on_connected(): if self.on_connected: self.on_connected() else: logger.debug("IoTHub Pipeline was connected, but no handler was set") def _on_disconnected(): if self.on_disconnected: self.on_disconnected() else: logger.debug("IoTHub Pipeline was disconnected, but no handler was set") def _on_new_sastoken_required(): if self.on_new_sastoken_required: self.on_new_sastoken_required() else: logger.debug("IoTHub Pipeline requires new SASToken, but no handler was set") def _on_background_exception(e): if self.on_background_exception: self.on_background_exception(e) else: logger.debug( "IoTHub Pipeline experienced background exception, but no handler was set" ) # Set internal event handlers self._pipeline.on_pipeline_event_handler = _on_pipeline_event self._pipeline.on_connected_handler = _on_connected self._pipeline.on_disconnected_handler = _on_disconnected self._pipeline.on_new_sastoken_required_handler = _on_new_sastoken_required self._pipeline.on_background_exception_handler = _on_background_exception # Initialize the pipeline callback = EventedCallback() op = pipeline_ops_base.InitializePipelineOperation(callback=callback) self._pipeline.run_op(op) callback.wait_for_completion() # Set the running flag self._running = True
def __init__(self, pipeline_configuration): """ Constructor for instantiating a pipeline adapter object :param auth_provider: The authentication provider :param pipeline_configuration: The configuration generated based on user inputs """ self.feature_enabled = { constant.C2D_MSG: False, constant.INPUT_MSG: False, constant.METHODS: False, constant.TWIN: False, constant.TWIN_PATCHES: False, } # Event Handlers - Will be set by Client after instantiation of this object self.on_connected = None self.on_disconnected = None self.on_c2d_message_received = None self.on_input_message_received = None self.on_method_request_received = None self.on_twin_patch_received = None # Currently a single timeout stage and a single retry stage for MQTT retry only. # Later, a higher level timeout and a higher level retry stage. self._pipeline = ( # # The root is always the root. By definition, it's the first stage in the pipeline. # pipeline_stages_base.PipelineRootStage(pipeline_configuration) # # SasTokenRenewalStage comes near the root by default because it should be as close # to the top of the pipeline as possible, and does not need to be after anything. # .append_stage(pipeline_stages_base.SasTokenRenewalStage()) # # EnsureDesiredPropertiesStage needs to be above TwinRequestResponseStage because it # sends GetTwinOperation ops and that stage handles those ops. # .append_stage( pipeline_stages_iothub.EnsureDesiredPropertiesStage()) # # TwinRequestResponseStage comes near the root by default because it doesn't need to be # after anything # .append_stage(pipeline_stages_iothub.TwinRequestResponseStage()) # # CoordinateRequestAndResponseStage needs to be after TwinRequestResponseStage because # TwinRequestResponseStage creates the request ops that CoordinateRequestAndResponseStage # is coordinating. It needs to be before IoTHubMQTTTranslationStage because that stage # operates on ops that CoordinateRequestAndResponseStage produces # .append_stage( pipeline_stages_base.CoordinateRequestAndResponseStage()) # # IoTHubMQTTTranslationStage comes here because this is the point where we can translate # all operations directly into MQTT. After this stage, only pipeline_stages_base stages # are allowed because IoTHubMQTTTranslationStage removes all the IoTHub-ness from the ops # .append_stage( pipeline_stages_iothub_mqtt.IoTHubMQTTTranslationStage()) # # AutoConnectStage comes here because only MQTT ops have the need_connection flag set # and this is the first place in the pipeline wherer we can guaranetee that all network # ops are MQTT ops. # .append_stage(pipeline_stages_base.AutoConnectStage()) # # ReconnectStage needs to be after AutoConnectStage because ReconnectStage sets/clears # the virtually_conencted flag and we want an automatic connection op to set this flag so # we can reconnect autoconnect operations. This is important, for example, if a # send_message causes the transport to automatically connect, but that connection fails. # When that happens, the ReconenctState will hold onto the ConnectOperation until it # succeeds, and only then will return success to the AutoConnectStage which will # allow the publish to continue. # .append_stage(pipeline_stages_base.ReconnectStage()) # # ConnectionLockStage needs to be after ReconnectStage because we want any ops that # ReconnectStage creates to go through the ConnectionLockStage gate # .append_stage(pipeline_stages_base.ConnectionLockStage()) # # RetryStage needs to be near the end because it's retrying low-level MQTT operations. # .append_stage(pipeline_stages_base.RetryStage()) # # OpTimeoutStage needs to be after RetryStage because OpTimeoutStage returns the timeout # errors that RetryStage is watching for. # .append_stage(pipeline_stages_base.OpTimeoutStage()) # # MQTTTransportStage needs to be at the very end of the pipeline because this is where # operations turn into network traffic # .append_stage(pipeline_stages_mqtt.MQTTTransportStage())) def _on_pipeline_event(event): if isinstance(event, pipeline_events_iothub.C2DMessageEvent): if self.on_c2d_message_received: self.on_c2d_message_received(event.message) else: logger.error( "C2D message event received with no handler. dropping." ) elif isinstance(event, pipeline_events_iothub.InputMessageEvent): if self.on_input_message_received: self.on_input_message_received(event.message) else: logger.error( "input message event received with no handler. dropping." ) elif isinstance(event, pipeline_events_iothub.MethodRequestEvent): if self.on_method_request_received: self.on_method_request_received(event.method_request) else: logger.error( "Method request event received with no handler. Dropping." ) elif isinstance( event, pipeline_events_iothub.TwinDesiredPropertiesPatchEvent): if self.on_twin_patch_received: self.on_twin_patch_received(event.patch) else: logger.error( "Twin patch event received with no handler. Dropping.") else: logger.error("Dropping unknown pipeline event {}".format( event.name)) def _on_connected(): if self.on_connected: self.on_connected() def _on_disconnected(): if self.on_disconnected: self.on_disconnected() # Set internal event handlers self._pipeline.on_pipeline_event_handler = _on_pipeline_event self._pipeline.on_connected_handler = _on_connected self._pipeline.on_disconnected_handler = _on_disconnected # Initialize the pipeline callback = EventedCallback() op = pipeline_ops_base.InitializePipelineOperation(callback=callback) self._pipeline.run_op(op) callback.wait_for_completion() # Set the running flag self._running = True