def stop_service(service_name, kill=False, message_service=None): """ Stop a specific service. :param service_name: The name of the service. :type service_name: str :param kill: ``True`` to force the service to stop. ``False`` to gently inform the service that it should stop. :type kill: bool :param message_service: An instance of :class:`systemlink.messagebus.message_service.MessageService`. May be ``None`` in which case a temporary instance will be used. :type message_service: systemlink.messagebus.message_service.MessageService or None """ if message_service: own_message_service = False else: own_message_service = True connection_manager = AmqpConnectionManager() builder = MessageServiceBuilder('ServiceManagerClient') builder.connection_manager = connection_manager message_service = MessageService(builder) try: broadcast = service_manager_messages.SvcMgrStopMultipleServicesBroadcast( service_name, '', kill) message_service.publish_broadcast(broadcast) finally: if own_message_service: message_service.close() message_service = None connection_manager.close() connection_manager = None
def _setup_amqp_connection(): ''' Opens the AMQP connection and instantiates a message service ''' try: global CONNECTION_MANAGER # pylint: disable=global-statement global MESSAGE_SERVICE # pylint: disable=global-statement if MESSAGE_SERVICE: MESSAGE_SERVICE.close() MESSAGE_SERVICE = None if CONNECTION_MANAGER: CONNECTION_MANAGER.close() CONNECTION_MANAGER = None master_config = AmqpConfigurationManager.get_configuration( id_=SKYLINE_MASTER_CONFIGURATION_ID, enable_fallbacks=False) service_name = 'SaltCalibrationMonitoring' connection_timeout = 50 CONNECTION_MANAGER = AmqpConnectionManager(config=master_config) CONNECTION_MANAGER.connection_timeout = connection_timeout CONNECTION_MANAGER.auto_reconnect = False message_service_builder = MessageServiceBuilder(service_name) message_service_builder.connection_manager = CONNECTION_MANAGER MESSAGE_SERVICE = MessageService(message_service_builder) except Exception as exc: # pylint: disable=broad-except log.error( 'Unexpected exception in "nisysmgmt_calibration_monitoring._setup_amqp_connection": %s', exc, exc_info=True) _set_disconnected_beacon_interval()
def _process_message(self, message): # pylint: disable=too-many-branches """ Used to allow invoking user defined callbacks for incoming messages at various levels. Only one is invoked per message, in the following priority: 1) Callbacks with ``state`` specific to the corrlation ID. 2) Callbacks without ``state`` specific to the message name. 3) General callbacks without ``state`` (user defined data). :param message: The incoming message to process. :type message: systemlink.messagebus.generic_message.GenericMessage """ msg_name_callback_info = None corr_id_info = None if message.correlation_id in self._corr_id_callbacks: corr_id_info = self._corr_id_callbacks[message.correlation_id] del self._corr_id_callbacks[message.correlation_id] if message.message_name in self._msg_name_callbacks: msg_name_callback_info = self._msg_name_callbacks[ message.message_name] if corr_id_info is not None: corr_id_info.callback(message, corr_id_info._state) # pylint: disable=protected-access elif msg_name_callback_info is not None: if self._explicit_ack and not msg_name_callback_info.explicit_ack: self.acknowledge_message(message.consumer_tag, message.deliver_tag) if msg_name_callback_info.callback is not None: msg_name_callback_info.callback(message) elif self._callback is not None: self._callback(message) elif message.has_error(): msg = ('Unhandled message ' + message.message_name + ' Origin=' + message.origin + ' Error=' + str(message.error)) LOGGER.error(msg) if self._trace_logger is not None: self._trace_logger.log_error(msg, skip_if_has_log_handler=True) elif self._trace_logger is not None and self._trace_unhandled_message.is_enabled: self._trace_logger.log(self._trace_unhandled_message, message.message_name) else: # pylint: disable=fixme # TODO add support for creating a unhandled message callback. # pylint: enable=fixme msg = 'Unhandled message: ' + message.message_name if self._amqp_connection_manager is not None: self._amqp_connection_manager.put_status_message( msg, False, replace_if_full=True) else: AmqpConnectionManager.get_instance().put_status_message( msg, False, replace_if_full=True) self._purge_correlation_id_callbacks()
def __init__(self, builder=None): """ :param builder: A :class:`systemlink.messagebus.message_publisher_builder.MessagePublisherBuilder` object used in the construction of this object. May be ``None`` if default behavior is desired. :type builder: systemlink.messagebus.message_publisher_builder.MessagePublisherBuilder """ if builder is None: builder = MessagePublisherBuilder() self._connection_manager = builder.connection_manager if self._connection_manager is None: self._connection_manager = AmqpConnectionManager.get_instance() LOGGER.debug('MessagePublisher\'s connection_manager: %s', self._connection_manager) self._origin_name = builder.origin self._reply_to = builder.reply_to self._exchange_name = self._connection_manager.exchange_name self._channel = self._connection_manager.create_publisher_channel() self._trace_logger = builder.trace_logger self._trace_raw_messages = None if self._trace_logger: self._trace_raw_messages = self._trace_logger.make_trace_point( 'RawMessages')
def _setup_amqp_connection(self): ''' Open the AMQP connection and instantiate the message service ''' master_config = AmqpConfigurationManager.get_configuration( id_=SKYLINE_MASTER_CONFIGURATION_ID, enable_fallbacks=False) service_name = 'AssetPerformanceManagementSaltService' connection_timeout = 50 self._connection_manager = AmqpConnectionManager(config=master_config) self._connection_manager.connection_timeout = connection_timeout self._connection_manager.auto_reconnect = False message_service_builder = MessageServiceBuilder(service_name) message_service_builder.connection_manager = self._connection_manager self._message_service = MessageService(message_service_builder)
def start_service(service_name, message_service=None): """ Start a specific service. :param service_name: The name of the service. :type service_name: str :param message_service: An instance of :class:`systemlink.messagebus.message_service.MessageService`. May be ``None`` in which case a temporary instance will be used. :type message_service: systemlink.messagebus.message_service.MessageService or None :param silent: If ``False``, will print status to stdout/stderr. If ``True``, will not print status. :type silent: bool """ if message_service: own_message_service = False else: own_message_service = True connection_manager = AmqpConnectionManager() builder = MessageServiceBuilder('ServiceManagerClient') builder.connection_manager = connection_manager message_service = MessageService(builder) try: node_name = None services = get_services(message_service=message_service) for service in services: # pylint: disable=not-an-iterable if service.name == service_name: node_name = service.node_name break if node_name is None: error_info = 'Service name "{0}" not found.'.format(service_name) raise SystemLinkException.from_name('Skyline.Exception', info=error_info) broadcast = service_manager_messages.SvcMgrStartServicesBroadcast( False, [node_name], False, [service_name], 1) message_service.publish_broadcast(broadcast) finally: if own_message_service: message_service.close() message_service = None connection_manager.close() connection_manager = None
def get_services(message_service=None): """ Get information for all services. :param message_service: An instance of :class:`systemlink.messagebus.message_service.MessageService`. May be ``None`` in which case a temporary instance will be used. :type message_service: systemlink.messagebus.message_service.MessageService or None :return: A list of service information. :rtype: list(systemlink.messagebus.service_manager_messages.ServiceInstanceBase) """ if message_service: own_message_service = False else: own_message_service = True connection_manager = AmqpConnectionManager() builder = MessageServiceBuilder('ServiceManagerClient') builder.connection_manager = connection_manager message_service = MessageService(builder) try: request = service_manager_messages.SvcMgrGetServiceInfoSnapshotRequest( ) generic_message = message_service.publish_synchronous_message(request) if generic_message is None: raise SystemLinkException.from_name('Skyline.RequestTimedOut') if generic_message.has_error(): raise SystemLinkException(error=generic_message.error) response = service_manager_messages.SvcMgrGetServiceInfoSnapshotResponse.from_message( generic_message) return response.services finally: if own_message_service: message_service.close() message_service = None connection_manager.close() connection_manager = None
def __init__(self, # pylint: disable=too-many-arguments message_service=None, service_name='TestMonitorClient', config=None, connection_timeout=5, auto_reconnect=True): """ :param message_service: An instance of the message service to use or ``None`` to allow this object to create and own the message service. :type message_service: systemlink.messagebus.message_service.MessageService or None :param service_name: If `message_service` is ``None`` and therefore this object creates and owns the message service, the name to use for this message service. Try to pick a unique name for your client. :type service_name: str or None :param config: If ``message_service`` is ``None``, the configuration to use for this message service. If this is ``None``, will use the default configuration. :type config: systemlink.messagebus.amqp_configuration.AmqpConfiguration or None :param connection_timeout: Timeout, in seconds, to use when trying to connect to the message broker. :type connection_timeout: float or int """ self._closing = False self._own_message_service = False self._connection_manager = None if message_service: self._message_service = message_service else: self._connection_manager = AmqpConnectionManager(config=config) self._connection_manager.connection_timeout = connection_timeout self._connection_manager.auto_reconnect = auto_reconnect message_service_builder = MessageServiceBuilder(service_name) message_service_builder.connection_manager = self._connection_manager self._message_service = MessageService(message_service_builder) self._own_message_service = True
def __init__(self, builder): """ :param builder: A :class:`systemlink.messagebus.amqp_consumer_builder.AmqpConsumerBuilder` object used in the construction of this object. :type builder: systemlink.messagebus.amqp_consumer_builder.AmqpConsumerBuilder """ self._closing = False self._trace_logger = None self._trace_raw_messages = None self._queue_name = builder.queue_name self._routing_key_prefix = builder.routing_key_prefix self._durable_queue = builder.durable_queue self._callback = builder.callback self._message_returned_callback = builder.message_returned_callback self._event_args_callback = builder.event_args_callback self._auto_reconnect = builder.auto_reconnect self._message_handling_thread_should_stop = False # pylint: disable=invalid-name self._monitoring_thread_should_stop = False self._should_handle_messages = False self._is_handling_messages = False self._channel = None self._monitoring_thread = None self._message_handling_thread = None self._connection_manager = builder.connection_manager if self._connection_manager is None: self._connection_manager = AmqpConnectionManager.get_instance() self._exchange_name = self._connection_manager.exchange_name self.trace_logger = builder.trace_logger self._explicit_ack = builder.explicit_ack self._channel = self._connection_manager.create_consumer_channel() self._connection_manager.queue_declare(self._channel, self._queue_name, self._durable_queue, False, not self._durable_queue) self._channel.queue_name = self._queue_name self._connection_manager.basic_qos(self._channel, 0, builder.prefetch_queue_depth, False) self._consumer_tag = '' self._monitoring_thread = None if self._auto_reconnect: self._monitoring_thread = threading.Thread( target=self._monitoring_thread_func) self._monitoring_thread.daemon = True self._monitoring_thread.start() self._message_handling_thread = threading.Thread( target=self._message_handling_thread_func) self._message_handling_thread.daemon = True self._message_handling_thread.start()
class AssetPerformanceManagmentAmqpWriter(object): # pylint: disable=too-few-public-methods ''' Abstraction over AMQP connection which facilitates communication between the minion and the AssetPerformanceManagement service. ''' __instance = None @staticmethod def has_systemlink_sdk(): ''' Check if the module sucessfully loaded NI Skyline Message Bus ''' return HAS_SYSTEMLINK_SDK def __new__(cls): ''' Handle instantiation so that only a single instance of this class is created ''' if not AssetPerformanceManagmentAmqpWriter.has_systemlink_sdk(): raise AssetPerformanceManagmentAmqpWriterException( 'Import of NI Skyline Message Bus failed.') if AssetPerformanceManagmentAmqpWriter.__instance is None: AssetPerformanceManagmentAmqpWriter.__instance = object.__new__( cls) # pylint: disable=protected-access AssetPerformanceManagmentAmqpWriter.__instance._initialized = False AssetPerformanceManagmentAmqpWriter.__instance._connection_manager = None AssetPerformanceManagmentAmqpWriter.__instance._message_service = None # pylint: enable=protected-access return AssetPerformanceManagmentAmqpWriter.__instance def __init__(self): ''' Initialize the message service if not already initialized ''' if not self._initialized: try: self._initialize_message_service() except SystemLinkException as exc: self._close_message_service() if exc.error.name == 'Skyline.AMQPErrorFailedToLogIn': # The salt-master may have changed credentials after the salt-minion # connects to the salt-master through Salt. raise AssetPerformanceManagmentAmqpWriterException( 'AMQP Authentication error. Credentials may have changed', exc, is_warning=True) else: # All other AMQP exceptions raise AssetPerformanceManagmentAmqpWriterException( 'An AMQP error has occurred', exc) def _initialize_message_service(self): ''' Initialize the message service :return: ``True`` if initialized successfully, ``False`` otherwise. :rtype: bool ''' file_path = paths.get_skyline_master_file() if not os.path.isfile(file_path): # The Skyline Master file is not available. # Can't set up the Message Service without it. return self._setup_amqp_connection() self._initialized = True def _setup_amqp_connection(self): ''' Open the AMQP connection and instantiate the message service ''' master_config = AmqpConfigurationManager.get_configuration( id_=SKYLINE_MASTER_CONFIGURATION_ID, enable_fallbacks=False) service_name = 'AssetPerformanceManagementSaltService' connection_timeout = 50 self._connection_manager = AmqpConnectionManager(config=master_config) self._connection_manager.connection_timeout = connection_timeout self._connection_manager.auto_reconnect = False message_service_builder = MessageServiceBuilder(service_name) message_service_builder.connection_manager = self._connection_manager self._message_service = MessageService(message_service_builder) def _close_message_service(self): ''' Close the AMQP connection and the message service ''' if self._message_service: self._message_service.close() self._message_service = None if self._connection_manager: self._connection_manager.close() self._connection_manager = None self._initialized = False @classmethod def cleanup(cls): ''' Clean up the writer by closing the connection ''' # This makes sure that if the salt-minion closes before the writer has been initialized # we don't create it just to clean it up. if cls.__instance is not None: cls.__instance._close_message_service() # pylint: disable=protected-access cls.__instance = None def publish_minion_assets_updated_broadcast(self, broadcast): # pylint: disable=invalid-name ''' Publish an AssetPerformanceManagementMinionAssetsUpdatedBroadcast over AMQP ''' try: self._message_service.publish_broadcast(broadcast) except SystemLinkException as exc: raise AssetPerformanceManagmentAmqpWriterException( 'An AMQP error has occurred', exc)
class TestMonitorClient(): """ Class to publicly access the Test Monitor Client. """ def __init__(self, # pylint: disable=too-many-arguments message_service=None, service_name='TestMonitorClient', config=None, connection_timeout=5, auto_reconnect=True): """ :param message_service: An instance of the message service to use or ``None`` to allow this object to create and own the message service. :type message_service: systemlink.messagebus.message_service.MessageService or None :param service_name: If `message_service` is ``None`` and therefore this object creates and owns the message service, the name to use for this message service. Try to pick a unique name for your client. :type service_name: str or None :param config: If ``message_service`` is ``None``, the configuration to use for this message service. If this is ``None``, will use the default configuration. :type config: systemlink.messagebus.amqp_configuration.AmqpConfiguration or None :param connection_timeout: Timeout, in seconds, to use when trying to connect to the message broker. :type connection_timeout: float or int """ self._closing = False self._own_message_service = False self._connection_manager = None if message_service: self._message_service = message_service else: self._connection_manager = AmqpConnectionManager(config=config) self._connection_manager.connection_timeout = connection_timeout self._connection_manager.auto_reconnect = auto_reconnect message_service_builder = MessageServiceBuilder(service_name) message_service_builder.connection_manager = self._connection_manager self._message_service = MessageService(message_service_builder) self._own_message_service = True def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): self.close() def __del__(self): self.close() def close(self): """ Close the TestMonitorClient and all associated resources. """ if self._closing: return self._closing = True if self._own_message_service: self._message_service.close() self._connection_manager.close() def create_results(self, results): """ Create one or more test results :param results: A list of dicts. Each dict must have the following keys: status (:class:`systemlink.testmonclient.messages.Status` or `str`): Test status. startedAt (:class:`datetime`): Test start time. programName (:class:`str`): Test program name. systemId (:class:`str`): Identifier for the system that ran this test. hostName (:class:`str`): Host machine name. operator (:class:`str`): Operator name for this result. serialNumber (:class:`str`): Serial number for the Unit Under Test. totalTimeInSeconds (:class:`float` or :class:`int`): Test duration time in seconds. keywords (``list(str)``): A list of keywords associated with the result. properties (``dict(str)``): Key/value pairs of properties associated with the result. fileIds (``list(str)``): List of fileIds associated with the result. :type results: list(dict) """ result_create_requests = [] for test in results: test_status = test['status'] if isinstance(test_status, str): test_status = testmon_messages.Status( testmon_messages.StatusType.from_string(test_status.upper()), test_status) test['status'] = test_status.to_dict() result_create_request = testmon_messages.ResultCreateRequest.from_dict(test) result_create_requests.append(result_create_request) request = testmon_messages.TestMonitorCreateTestResultsRequest(result_create_requests) generic_message = self._message_service.publish_synchronous_message(request) if generic_message is None: raise SystemLinkException.from_name('Skyline.RequestTimedOut') if generic_message.has_error(): raise SystemLinkException(error=generic_message.error) LOGGER.debug('generic_message = %s', generic_message) res = testmon_messages.TestMonitorCreateTestResultsResponse.from_message(generic_message) LOGGER.debug('message = %s', res) return res def update_results(self, updates, replace=False, determine_status_from_steps=False): """ Update one or more test results :param updates: A list of dicts. Each dict must have the following keys: status (:class:`systemlink.testmonclient.messages.Status` or `str`): Test status. startedAt (:class:`datetime`): Test start time. programName (:class:`str`): Test program name. systemId (:class:`str`): Identifier for the system that ran this test. operator (:class:`str`): Operator name for this result. serialNumber (:class:`str`): Serial number for the Unit Under Test. totalTimeInSeconds (:class:`float` or :class:`int`): Test duration time in seconds. keywords (``list(str)``): A list of keywords associated with the result. properties (``dict(str)``): Key/value pairs of properties associated with the result. fileIds (``list(str)``): List of fileIds associated with the result. :type updates: list(dict) :param replace: Indicates if keywords, properties, and file ids should replace the existing collection, or be merged with the existing collection. :type replace: bool :param determine_status_from_steps: Indicates if the status should be set based on the status of the related steps. :type determine_status_from_steps: bool """ result_update_requests = [] for update in updates: test_status = update['status'] if isinstance(test_status, str): test_status = testmon_messages.Status( testmon_messages.StatusType.from_string(test_status.upper()), test_status) update['status'] = test_status.to_dict() result_update_request = testmon_messages.ResultUpdateRequest.from_dict(update) result_update_requests.append(result_update_request) request = testmon_messages.TestMonitorUpdateTestResultsRequest( result_update_requests, replace, determine_status_from_steps) generic_message = self._message_service.publish_synchronous_message(request) if generic_message is None: raise SystemLinkException.from_name('Skyline.RequestTimedOut') if generic_message.has_error(): raise SystemLinkException(error=generic_message.error) LOGGER.debug('generic_message = %s', generic_message) res = testmon_messages.TestMonitorUpdateTestResultsResponse.from_message(generic_message) LOGGER.debug('message = %s', res) return res def delete_results(self, ids, delete_steps=True): """ Delete one or more test results :param ids: A list of the IDs of the results to delete. :type ids: list(str) :param delete_steps: Whether or not to delete the results corresponding steps. :type delete_steps: bool """ request = testmon_messages.TestMonitorDeleteResultsRequest(ids, delete_steps) generic_message = self._message_service.publish_synchronous_message(request) if generic_message is None: raise SystemLinkException.from_name('Skyline.RequestTimedOut') if generic_message.has_error(): raise SystemLinkException(error=generic_message.error) LOGGER.debug('generic_message = %s', generic_message) res = testmon_messages.TestMonitorDeleteResultsResponse.from_message(generic_message) LOGGER.debug('message = %s', res) return res def delete_all_results(self): """ Delete all results """ routed_message = testmon_messages.TestMonitorDeleteAllResultsRoutedMessage() self._message_service.publish_routed_message(routed_message) def query_results(self, query=None, skip=0, take=-1): """ Return results that match query :param query: Object indicating query parameters. :type query: systemlink.testmonclient.messages.ResultQuery :param skip: Number of results to skip before searching. :type skip: int :param take: Maximum number of results to return. :type take: int :return: Results that matched the query. :rtype: tuple(list(systemlink.testmonclient.messages.ResultResponse), int) """ request = testmon_messages.TestMonitorQueryResultsRequest(query, skip, take) generic_message = self._message_service.publish_synchronous_message(request) if generic_message is None: raise SystemLinkException.from_name('Skyline.RequestTimedOut') if generic_message.has_error(): raise SystemLinkException(error=generic_message.error) LOGGER.debug('generic_message = %s', generic_message) res = testmon_messages.TestMonitorQueryResultsResponse.from_message(generic_message) LOGGER.debug('TotalCount: %d', res.total_count) return res.results, res.total_count def create_steps(self, steps): """ Create one or more step results :param steps: A list of dicts. Each dict must have the following keys: name (:class:`str`): Step name. stepType (:class:`str`): Step type. stepId (:class:`str`): Step Identifier. parentId (:class:`str`): Identifier for the step parent. resultId (:class:`str`): Identifier for the result that this step is associated with. status (:class:`systemlink.testmonclient.messages.Status` or `str`): Step status. totalTimeInSeconds (:class:`float` or :class:`int`): Step duration time in seconds. startedAt (:class:`datetime`): Step start time. dataModel (:class:`str`): The name of the data structure in the stepData list. This value identifies the key names that exist in the stepData parameters object. It is generally used to provide context for custom UIs to know what values are expected. stepData (``list(stepData)``): A list of data objects for the step. Each element contains a text string and a parameters dictionary of string:string key-value pairs. children (``list(str)``): A list of step ids that define other steps in the request that are children of this step. The ids in this list must exist as objects in the in the request. """ step_create_requests = [] for step in steps: step_status = step['status'] if isinstance(step_status, str): step_status = testmon_messages.Status( testmon_messages.StatusType.from_string(step_status.upper()), step_status) step['status'] = step_status.to_dict() step_create_request = testmon_messages.StepCreateRequest.from_dict(step) step_create_requests.append(step_create_request) request = testmon_messages.TestMonitorCreateTestStepsRequest(step_create_requests) generic_message = self._message_service.publish_synchronous_message(request) if generic_message is None: raise SystemLinkException.from_name('Skyline.RequestTimedOut') if generic_message.has_error(): raise SystemLinkException(error=generic_message.error) LOGGER.debug('generic_message = %s', generic_message) res = testmon_messages.TestMonitorCreateTestStepsResponse.from_message(generic_message) LOGGER.debug('message = %s', res) return res def update_steps(self, steps): """ Update one or more steps :param updates: A list of dicts. Each dict must at least have step_id and result_id. The other dict members are used to update the step, if present: name (:class:`str`): Step name. stepType (:class:`str`): Step type stepId (:class:`str`): Step Identifier. parentId (:class:`str`): Identifier for the step parent. resultId (:class:`str`): Identifier for the result that this step is associated with. status (:class:`systemlink.testmonclient.messages.Status` or `str`): Step status. totalTimeInSeconds (:class:`float` or :class:`int`): Step duration time in seconds. startedAt (:class:`datetime`): Step start time. dataModel (:class:`str`): The name of the data structure in the stepData list. This value identifies the key names that exist in the stepData parameters object. It is generally used to provide context for custom UIs to know what values are expected. stepData (``list(stepData)``): A list of data objects for the step. Each element contains a text string and a parameters dictionary of string:string key-value pairs. children (``list(str)``): A list of step ids that define other steps in the request that are children of this step. The ids in this list must exist as objects in the in the request. """ step_update_requests = [] for step in steps: if 'status' in step: step_status = step['status'] if isinstance(step_status, str): step_status = testmon_messages.Status( testmon_messages.StatusType.from_string(step_status.upper()), step_status) step['status'] = step_status.to_dict() step_update_request = testmon_messages.StepUpdateRequest.from_dict(step) step_update_requests.append(step_update_request) request = testmon_messages.TestMonitorUpdateTestStepsRequest(step_update_requests) generic_message = self._message_service.publish_synchronous_message(request) if generic_message is None: raise SystemLinkException.from_name('Skyline.RequestTimedOut') if generic_message.has_error(): raise SystemLinkException(error=generic_message.error) LOGGER.debug('generic_message = %s', generic_message) res = testmon_messages.TestMonitorUpdateTestStepsResponse.from_message(generic_message) LOGGER.debug('message = %s', res) return res def delete_steps(self, steps): """ Delete one or more steps :param steps: A list of dicts. Each dict must have the following keys: stepId (:class:`str`): Step Identifier. resultId (:class:`str`): Identifier for the result that this step is associated with. :type steps: list(dict) """ delete_steps = [] for step in steps: delete_step = testmon_messages.StepDeleteRequest.from_dict(step) delete_steps.append(delete_step) request = testmon_messages.TestMonitorDeleteStepsRequest(delete_steps) generic_message = self._message_service.publish_synchronous_message(request) if generic_message is None: raise SystemLinkException.from_name('Skyline.RequestTimedOut') if generic_message.has_error(): raise SystemLinkException(error=generic_message.error) LOGGER.debug('generic_message = %s', generic_message) res = testmon_messages.TestMonitorDeleteStepsResponse.from_message(generic_message) LOGGER.debug('message = %s', res) return res def query_steps(self, query=None, skip=0, take=-1): """ Return steps that match query :param query: Object indicating query parameters. :type query: systemlink.testmonclient.messages.StepQuery :param skip: Number of steps to skip before searching. :type skip: int :param take: Maximum number of steps to return. :type take: int :return: Results that matched the query. :rtype: tuple(list(systemlink.testmonclient.messages.StepResponse), int) """ request = testmon_messages.TestMonitorQueryStepsRequest(query, skip, take) generic_message = self._message_service.publish_synchronous_message(request) if generic_message is None: raise SystemLinkException.from_name('Skyline.RequestTimedOut') if generic_message.has_error(): raise SystemLinkException(error=generic_message.error) LOGGER.debug('generic_message = %s', generic_message) res = testmon_messages.TestMonitorQueryStepsResponse.from_message(generic_message) LOGGER.debug('TotalCount: %d', res.total_count) return res.steps, res.total_count