def _execute_op(self, op): def map_twin_error(error, twin_op): if error: return 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)) return exceptions.ServiceError( "twin operation returned status {}".format(twin_op.status_code) ) if isinstance(op, pipeline_ops_iothub.GetTwinOperation): def on_twin_response(twin_op, error): logger.debug("{}({}): Got response for GetTwinOperation".format(self.name, op.name)) error = map_twin_error(error=error, twin_op=twin_op) if not error: op.twin = json.loads(twin_op.response_body.decode("utf-8")) self.complete_op(op, error=error) self.send_op_down( pipeline_ops_base.RequestAndResponseOperation( 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, error): logger.debug( "{}({}): Got response for PatchTwinReportedPropertiesOperation operation".format( self.name, op.name ) ) error = map_twin_error(error=error, twin_op=twin_op) self.complete_op(op, error=error) logger.debug( "{}({}): Sending reported properties patch: {}".format(self.name, op.name, op.patch) ) self.send_op_down( pipeline_ops_base.RequestAndResponseOperation( request_type=constant.TWIN, method="PATCH", resource_location="/properties/reported/", request_body=json.dumps(op.patch), callback=on_twin_response, ) ) else: self.send_op_down(op)
def make_fake_request_and_response(mocker): return pipeline_ops_base.RequestAndResponseOperation( request_type=fake_request_type, method=fake_method, resource_location=fake_resource_location, request_body=fake_request_body, callback=mocker.MagicMock(), )
def _run_op(self, op): if isinstance(op, pipeline_ops_provisioning.RegisterOperation): initial_register_op = op self_weakref = weakref.ref(self) @pipeline_thread.invoke_on_pipeline_thread_nowait def register_timeout(): this = self_weakref() logger.info( "{stage_name}({op_name}): returning timeout error".format( stage_name=this.name, op_name=op.name)) initial_register_op.complete(error=(exceptions.ServiceError( "Operation timed out before provisioning service could respond for {op_type} operation" .format(op_type=constant.REGISTER)))) logger.debug("{}({}): Creating provisioning timeout timer".format( self.name, op.name)) initial_register_op.provisioning_timeout_timer = Timer( constant.DEFAULT_TIMEOUT_INTERVAL, register_timeout) initial_register_op.provisioning_timeout_timer.start() def on_registration_response(op, error): self._clear_timeout_timer(initial_register_op, error) logger.debug( "{stage_name}({op_name}): Received response with status code {status_code} for RegisterOperation" .format(stage_name=self.name, op_name=op.name, status_code=op.status_code)) if error: logger.error( "{stage_name}({op_name}): Received error for {prov_op_name} operation" .format(stage_name=self.name, op_name=op.name, prov_op_name=op.request_type)) initial_register_op.complete(error=error) else: if 300 <= op.status_code < 429: self._process_service_error_status_code( initial_register_op, op) elif op.status_code >= 429: self._process_retry_status_code( error, initial_register_op, op) else: decoded_response = self._decode_response(op) operation_id = decoded_response.get( "operationId", None) registration_status = decoded_response.get( "status", None) if registration_status == "assigning": self_weakref = weakref.ref(self) def copy_result_to_original_op(op, error): logger.debug( "Copying registration result from Query Status Op to Registration Op" ) initial_register_op.registration_result = op.registration_result initial_register_op.error = error @pipeline_thread.invoke_on_pipeline_thread_nowait def do_query_after_interval(): this = self_weakref() initial_register_op.polling_timer.cancel() initial_register_op.polling_timer = None logger.info( "{stage_name}({op_name}): polling".format( stage_name=this.name, op_name=op.name)) query_worker_op = initial_register_op.spawn_worker_op( worker_op_type=pipeline_ops_provisioning. PollStatusOperation, request_payload=" ", operation_id=operation_id, callback=copy_result_to_original_op, ) self.send_op_down(query_worker_op) logger.warning( "{stage_name}({op_name}): Op will transition into polling after interval {interval}. Setting timer." .format( stage_name=self.name, op_name=op.name, interval=constant.DEFAULT_POLLING_INTERVAL, )) logger.debug( "{}({}): Creating polling timer".format( self.name, op.name)) initial_register_op.polling_timer = Timer( constant.DEFAULT_POLLING_INTERVAL, do_query_after_interval) initial_register_op.polling_timer.start() elif registration_status == "failed" or registration_status == "assigned": self._process_failed_and_assigned_registration_status( error=error, operation_id=operation_id, decoded_response=decoded_response, registration_status=registration_status, original_provisioning_op=initial_register_op, request_response_op=op, ) else: self._process_unknown_registration_status( registration_status=registration_status, original_provisioning_op=initial_register_op, request_response_op=op, ) registration_payload = DeviceRegistrationPayload( registration_id=initial_register_op.registration_id, custom_payload=initial_register_op.request_payload, ) self.send_op_down( pipeline_ops_base.RequestAndResponseOperation( request_type=constant.REGISTER, method="PUT", resource_location="/", request_body=registration_payload.get_json_string(), callback=on_registration_response, )) else: super(RegistrationStage, self)._run_op(op)
def _run_op(self, op): if isinstance(op, pipeline_ops_provisioning.PollStatusOperation): query_status_op = op self_weakref = weakref.ref(self) @pipeline_thread.invoke_on_pipeline_thread_nowait def query_timeout(): this = self_weakref() logger.info( "{stage_name}({op_name}): returning timeout error".format( stage_name=this.name, op_name=op.name)) query_status_op.complete(error=(exceptions.ServiceError( "Operation timed out before provisioning service could respond for {op_type} operation" .format(op_type=constant.QUERY)))) logger.debug("{}({}): Creating provisioning timeout timer".format( self.name, op.name)) query_status_op.provisioning_timeout_timer = Timer( constant.DEFAULT_TIMEOUT_INTERVAL, query_timeout) query_status_op.provisioning_timeout_timer.start() def on_query_response(op, error): self._clear_timeout_timer(query_status_op, error) logger.debug( "{stage_name}({op_name}): Received response with status code {status_code} for PollStatusOperation with operation id {oper_id}" .format( stage_name=self.name, op_name=op.name, status_code=op.status_code, oper_id=op.query_params["operation_id"], )) if error: logger.error( "{stage_name}({op_name}): Received error for {prov_op_name} operation" .format(stage_name=self.name, op_name=op.name, prov_op_name=op.request_type)) query_status_op.complete(error=error) else: if 300 <= op.status_code < 429: self._process_service_error_status_code( query_status_op, op) elif op.status_code >= 429: self._process_retry_status_code( error, query_status_op, op) else: decoded_response = self._decode_response(op) operation_id = decoded_response.get( "operationId", None) registration_status = decoded_response.get( "status", None) if registration_status == "assigning": polling_interval = ( int(op.retry_after, 10) if op.retry_after is not None else constant.DEFAULT_POLLING_INTERVAL) self_weakref = weakref.ref(self) @pipeline_thread.invoke_on_pipeline_thread_nowait def do_polling(): this = self_weakref() logger.info( "{stage_name}({op_name}): retrying".format( stage_name=this.name, op_name=op.name)) query_status_op.polling_timer.cancel() query_status_op.polling_timer = None query_status_op.completed = False this.run_op(query_status_op) logger.info( "{stage_name}({op_name}): Op needs retry with interval {interval} because of {error}. Setting timer." .format( stage_name=self.name, op_name=op.name, interval=polling_interval, error=error, )) logger.debug( "{}({}): Creating polling timer".format( self.name, op.name)) query_status_op.polling_timer = Timer( polling_interval, do_polling) query_status_op.polling_timer.start() elif registration_status == "assigned" or registration_status == "failed": self._process_failed_and_assigned_registration_status( error=error, operation_id=operation_id, decoded_response=decoded_response, registration_status=registration_status, original_provisioning_op=query_status_op, request_response_op=op, ) else: self._process_unknown_registration_status( registration_status=registration_status, original_provisioning_op=query_status_op, request_response_op=op, ) self.send_op_down( pipeline_ops_base.RequestAndResponseOperation( request_type=constant.QUERY, method="GET", resource_location="/", query_params={ "operation_id": query_status_op.operation_id }, request_body=query_status_op.request_payload, callback=on_query_response, )) else: super(PollingStatusStage, self)._run_op(op)
def _run_op(self, op): def map_twin_error(error, twin_op): if error: return 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)) return exceptions.ServiceError( "twin operation returned status {}".format( twin_op.status_code)) if isinstance(op, pipeline_ops_iothub.GetTwinOperation): # Alias to avoid overload within the callback below # CT-TODO: remove the need for this with better callback semantics op_waiting_for_response = op def on_twin_response(op, error): logger.debug( "{}({}): Got response for GetTwinOperation".format( self.name, op.name)) error = map_twin_error(error=error, twin_op=op) if not error: op_waiting_for_response.twin = json.loads( op.response_body.decode("utf-8")) op_waiting_for_response.complete(error=error) self.send_op_down( pipeline_ops_base.RequestAndResponseOperation( request_type=constant.TWIN, method="GET", resource_location="/", request_body=" ", callback=on_twin_response, )) elif isinstance( op, pipeline_ops_iothub.PatchTwinReportedPropertiesOperation): # Alias to avoid overload within the callback below # CT-TODO: remove the need for this with better callback semantics op_waiting_for_response = op def on_twin_response(op, error): logger.debug( "{}({}): Got response for PatchTwinReportedPropertiesOperation operation" .format(self.name, op.name)) error = map_twin_error(error=error, twin_op=op) op_waiting_for_response.complete(error=error) logger.debug( "{}({}): Sending reported properties patch: {}".format( self.name, op.name, op.patch)) self.send_op_down( pipeline_ops_base.RequestAndResponseOperation( request_type=constant.TWIN, method="PATCH", resource_location="/properties/reported/", request_body=json.dumps(op.patch), callback=on_twin_response, )) else: super(TwinRequestResponseStage, self)._run_op(op)