class ServiceBusClient(mixins.ServiceBusMixin): """A Service Bus client for a namespace with the specified SAS authentication settings. :param str service_namespace: Service Bus namespace, required for all operations. :param str host_base: Optional. Live host base URL. Defaults to Public Azure. :param str shared_access_key_name: SAS authentication key name. :param str shared_access_key_value: SAS authentication key value. :param int http_request_timeout: Optional. Timeout for the HTTP request, in seconds. Default value is 65 seconds. :param http_request_session: Optional. Session object to use for HTTP requests. :type http_request_session: ~requests.Session :param bool debug: Whether to output AMQP network trace to the logger. Example: .. literalinclude:: ../examples/test_examples.py :start-after: [START create_servicebus_client] :end-before: [END create_servicebus_client] :language: python :dedent: 4 :caption: Create a new instance of the Service Bus client """ def __init__(self, service_namespace=None, host_base=SERVICE_BUS_HOST_BASE, shared_access_key_name=None, shared_access_key_value=None, http_request_timeout=DEFAULT_HTTP_TIMEOUT, http_request_session=None, debug=False): self.service_namespace = service_namespace self.host_base = host_base self.shared_access_key_name = shared_access_key_name self.shared_access_key_value = shared_access_key_value self.debug = debug self.mgmt_client = ServiceBusService( service_namespace=service_namespace, host_base=host_base, shared_access_key_name=shared_access_key_name, shared_access_key_value=shared_access_key_value, timeout=http_request_timeout, request_session=http_request_session) @classmethod def from_connection_string(cls, conn_str, **kwargs): """Create a Service Bus client from a connection string. :param conn_str: The connection string. :type conn_str: str Example: .. literalinclude:: ../examples/test_examples.py :start-after: [START create_servicebus_client_connstr] :end-before: [END create_servicebus_client_connstr] :language: python :dedent: 4 :caption: Create a ServiceBusClient via a connection string. """ address, policy, key, _ = parse_conn_str(conn_str) parsed_namespace = urlparse(address) namespace, _, base = parsed_namespace.hostname.partition('.') return cls(namespace, shared_access_key_name=policy, shared_access_key_value=key, host_base='.' + base, **kwargs) def _get_host(self): return "sb://" + self.service_namespace + self.host_base def get_queue(self, queue_name): """Get a client for a queue entity. :param queue_name: The name of the queue. :type queue_name: str :rtype: ~azure.servicebus.servicebus_client.QueueClient :raises: ~azure.servicebus.common.errors.ServiceBusConnectionError if the namespace is not found. :raises: ~azure.servicebus.common.errors.ServiceBusResourceNotFound if the queue is not found. Example: .. literalinclude:: ../examples/test_examples.py :start-after: [START get_queue_client] :end-before: [END get_queue_client] :language: python :dedent: 8 :caption: Get the specific queue client from Service Bus client """ try: queue = self.mgmt_client.get_queue(queue_name) except requests.exceptions.ConnectionError as e: raise ServiceBusConnectionError( "Namespace: {} not found".format(self.service_namespace), e) except AzureServiceBusResourceNotFound: raise ServiceBusResourceNotFound( "Specificed queue does not exist.") return QueueClient.from_entity( self._get_host(), queue, shared_access_key_name=self.shared_access_key_name, shared_access_key_value=self.shared_access_key_value, mgmt_client=self.mgmt_client, debug=self.debug) def list_queues(self): """Get clients for all queue entities in the namespace. :rtype: list[~azure.servicebus.servicebus_client.QueueClient] :raises: ~azure.servicebus.common.errors.ServiceBusConnectionError if the namespace is not found. Example: .. literalinclude:: ../examples/test_examples.py :start-after: [START list_queues] :end-before: [END list_queues] :language: python :dedent: 4 :caption: List the queues from Service Bus client """ try: queues = self.mgmt_client.list_queues() except requests.exceptions.ConnectionError as e: raise ServiceBusConnectionError( "Namespace: {} not found".format(self.service_namespace), e) queue_clients = [] for queue in queues: queue_clients.append( QueueClient.from_entity( self._get_host(), queue, shared_access_key_name=self.shared_access_key_name, shared_access_key_value=self.shared_access_key_value, mgmt_client=self.mgmt_client, debug=self.debug)) return queue_clients def get_topic(self, topic_name): """Get a client for a topic entity. :param topic_name: The name of the topic. :type topic_name: str :rtype: ~azure.servicebus.servicebus_client.TopicClient :raises: ~azure.servicebus.common.errors.ServiceBusConnectionError if the namespace is not found. :raises: ~azure.servicebus.common.errors.ServiceBusResourceNotFound if the topic is not found. Example: .. literalinclude:: ../examples/test_examples.py :start-after: [START get_topic_client] :end-before: [END get_topic_client] :language: python :dedent: 8 :caption: Get the specific topic client from Service Bus client """ try: topic = self.mgmt_client.get_topic(topic_name) except requests.exceptions.ConnectionError as e: raise ServiceBusConnectionError( "Namespace: {} not found".format(self.service_namespace), e) except AzureServiceBusResourceNotFound: raise ServiceBusResourceNotFound( "Specificed topic does not exist.") return TopicClient.from_entity( self._get_host(), topic, shared_access_key_name=self.shared_access_key_name, shared_access_key_value=self.shared_access_key_value, debug=self.debug) def list_topics(self): """Get a client for all topic entities in the namespace. :rtype: list[~azure.servicebus.servicebus_client.TopicClient] :raises: ~azure.servicebus.common.errors.ServiceBusConnectionError if the namespace is not found. Example: .. literalinclude:: ../examples/test_examples.py :start-after: [START list_topics] :end-before: [END list_topics] :language: python :dedent: 4 :caption: List the topics from Service Bus client """ try: topics = self.mgmt_client.list_topics() except requests.exceptions.ConnectionError as e: raise ServiceBusConnectionError( "Namespace: {} not found".format(self.service_namespace), e) topic_clients = [] for topic in topics: topic_clients.append( TopicClient.from_entity( self._get_host(), topic, shared_access_key_name=self.shared_access_key_name, shared_access_key_value=self.shared_access_key_value, debug=self.debug)) return topic_clients def get_subscription(self, topic_name, subscription_name): """Get a client for a subscription entity. :param topic_name: The name of the topic. :type topic_name: str :param subscription_name: The name of the subscription. :type subscription_name: str :rtype: ~azure.servicebus.servicebus_client.SubscriptionClient :raises: ~azure.servicebus.common.errors.ServiceBusConnectionError if the namespace is not found. :raises: ~azure.servicebus.common.errors.ServiceBusResourceNotFound if the subscription is not found. Example: .. literalinclude:: ../examples/test_examples.py :start-after: [START get_subscription_client] :end-before: [END get_subscription_client] :language: python :dedent: 8 :caption: Get the specific subscription client from Service Bus client """ try: subscription = self.mgmt_client.get_subscription( topic_name, subscription_name) except requests.exceptions.ConnectionError as e: raise ServiceBusConnectionError( "Namespace: {} not found".format(self.service_namespace), e) except AzureServiceBusResourceNotFound: raise ServiceBusResourceNotFound( "Specificed subscription does not exist.") return SubscriptionClient.from_entity( self._get_host(), topic_name, subscription, shared_access_key_name=self.shared_access_key_name, shared_access_key_value=self.shared_access_key_value, debug=self.debug) def list_subscriptions(self, topic_name): """Get a client for all subscription entities in the topic. :param topic_name: The topic to list subscriptions for. :type topic_name: str :rtype: list[~azure.servicebus.servicebus_client.SubscriptionClient] :raises: ~azure.servicebus.common.errors.ServiceBusConnectionError if the namespace is not found. :raises: ~azure.servicebus.common.errors.ServiceBusResourceNotFound if the topic is not found. Example: .. literalinclude:: ../examples/test_examples.py :start-after: [START list_subscriptions] :end-before: [END list_subscriptions] :language: python :dedent: 4 :caption: List the subscriptions from Service Bus client """ try: subs = self.mgmt_client.list_subscriptions(topic_name) except requests.exceptions.ConnectionError as e: raise ServiceBusConnectionError( "Namespace: {} not found".format(self.service_namespace), e) except AzureServiceBusResourceNotFound: raise ServiceBusResourceNotFound( "Specificed topic does not exist.") sub_clients = [] for sub in subs: sub_clients.append( SubscriptionClient.from_entity( self._get_host(), topic_name, sub, shared_access_key_name=self.shared_access_key_name, shared_access_key_value=self.shared_access_key_value, debug=self.debug)) return sub_clients
class ServiceBusClient(mixins.ServiceBusMixin): """A Service Bus client for a namespace with the specified SAS authentication settings. :param str service_namespace: Service Bus namespace, required for all operations. :param str host_base: Optional. Live host base URL. Defaults to Azure URL. :param str shared_access_key_name: SAS authentication key name. :param str shared_access_key_value: SAS authentication key value. :param loop: An async event loop. :param int http_request_timeout: Optional. Timeout for the HTTP request, in seconds. :param http_request_session: Optional. Session object to use for HTTP requests. :param bool debug: Whether to output AMQP network trace to the logger. Example: .. literalinclude:: ../examples/async_examples/test_examples_async.py :start-after: [START create_async_servicebus_client] :end-before: [END create_async_servicebus_client] :language: python :dedent: 4 :caption: Create a ServiceBusClient. """ def __init__(self, *, service_namespace=None, host_base=SERVICE_BUS_HOST_BASE, shared_access_key_name=None, shared_access_key_value=None, loop=None, http_request_timeout=DEFAULT_HTTP_TIMEOUT, http_request_session=None, debug=False): self.loop = loop or get_running_loop() self.service_namespace = service_namespace self.host_base = host_base self.shared_access_key_name = shared_access_key_name self.shared_access_key_value = shared_access_key_value self.debug = debug self.mgmt_client = ServiceBusService( service_namespace=service_namespace, host_base=host_base, shared_access_key_name=shared_access_key_name, shared_access_key_value=shared_access_key_value, timeout=http_request_timeout, request_session=http_request_session) @classmethod def from_connection_string(cls, conn_str, *, loop=None, **kwargs): """Create a Service Bus client from a connection string. :param conn_str: The connection string. :type conn_str: str Example: .. literalinclude:: ../examples/async_examples/test_examples_async.py :start-after: [START create_async_servicebus_client_connstr] :end-before: [END create_async_servicebus_client_connstr] :language: python :dedent: 4 :caption: Create a ServiceBusClient via a connection string. """ address, policy, key, _ = parse_conn_str(conn_str) parsed_namespace = urlparse(address) namespace, _, base = parsed_namespace.hostname.partition('.') return cls( service_namespace=namespace, shared_access_key_name=policy, shared_access_key_value=key, host_base='.' + base, loop=loop, **kwargs) def get_queue(self, queue_name): """Get an async client for a queue entity. :param queue_name: The name of the queue. :type queue_name: str :rtype: ~azure.servicebus.aio.async_client.QueueClient :raises: ~azure.servicebus.common.errors.ServiceBusConnectionError if the namespace is not found. :raises: ~azure.servicebus.common.errors.ServiceBusResourceNotFound if the queue is not found. Example: .. literalinclude:: ../examples/async_examples/test_examples_async.py :start-after: [START get_async_queue_client] :end-before: [END get_async_queue_client] :language: python :dedent: 4 :caption: Get a QueueClient for the specified queue. """ try: queue = self.mgmt_client.get_queue(queue_name) except requests.exceptions.ConnectionError as e: raise ServiceBusConnectionError("Namespace: {} not found".format(self.service_namespace), e) except AzureServiceBusResourceNotFound: raise ServiceBusResourceNotFound("Specificed queue does not exist.") return QueueClient.from_entity( self._get_host(), queue, shared_access_key_name=self.shared_access_key_name, shared_access_key_value=self.shared_access_key_value, mgmt_client=self.mgmt_client, loop=self.loop, debug=self.debug) def list_queues(self): """Get async clients for all queue entities in the namespace. :rtype: list[~azure.servicebus.aio.async_client.QueueClient] :raises: ~azure.servicebus.common.errors.ServiceBusConnectionError if the namespace is not found. """ try: queues = self.mgmt_client.list_queues() except requests.exceptions.ConnectionError as e: raise ServiceBusConnectionError("Namespace: {} not found".format(self.service_namespace), e) queue_clients = [] for queue in queues: queue_clients.append(QueueClient.from_entity( self._get_host(), queue, shared_access_key_name=self.shared_access_key_name, shared_access_key_value=self.shared_access_key_value, mgmt_client=self.mgmt_client, loop=self.loop, debug=self.debug)) return queue_clients def get_topic(self, topic_name): """Get an async client for a topic entity. :param topic_name: The name of the topic. :type topic_name: str :rtype: ~azure.servicebus.aio.async_client.TopicClient :raises: ~azure.servicebus.common.errors.ServiceBusConnectionError if the namespace is not found. :raises: ~azure.servicebus.common.errors.ServiceBusResourceNotFound if the topic is not found. Example: .. literalinclude:: ../examples/async_examples/test_examples_async.py :start-after: [START get_async_topic_client] :end-before: [END get_async_topic_client] :language: python :dedent: 4 :caption: Get a TopicClient for the specified topic. """ try: topic = self.mgmt_client.get_topic(topic_name) except requests.exceptions.ConnectionError as e: raise ServiceBusConnectionError("Namespace: {} not found".format(self.service_namespace), e) except AzureServiceBusResourceNotFound: raise ServiceBusResourceNotFound("Specificed topic does not exist.") return TopicClient.from_entity( self._get_host(), topic, shared_access_key_name=self.shared_access_key_name, shared_access_key_value=self.shared_access_key_value, loop=self.loop, debug=self.debug) def list_topics(self): """Get an async client for all topic entities in the namespace. :rtype: list[~azure.servicebus.aio.async_client.TopicClient] :raises: ~azure.servicebus.common.errors.ServiceBusConnectionError if the namespace is not found. """ try: topics = self.mgmt_client.list_topics() except requests.exceptions.ConnectionError as e: raise ServiceBusConnectionError("Namespace: {} not found".format(self.service_namespace), e) topic_clients = [] for topic in topics: topic_clients.append(TopicClient.from_entity( self._get_host(), topic, shared_access_key_name=self.shared_access_key_name, shared_access_key_value=self.shared_access_key_value, loop=self.loop, debug=self.debug)) return topic_clients def get_subscription(self, topic_name, subscription_name): """Get an async client for a subscription entity. :param topic_name: The name of the topic. :type topic_name: str :param subscription_name: The name of the subscription. :type subscription_name: str :rtype: ~azure.servicebus.aio.async_client.SubscriptionClient :raises: ~azure.servicebus.common.errors.ServiceBusConnectionError if the namespace is not found. :raises: ~azure.servicebus.common.errors.ServiceBusResourceNotFound if the subscription is not found. Example: .. literalinclude:: ../examples/async_examples/test_examples_async.py :start-after: [START get_async_subscription_client] :end-before: [END get_async_subscription_client] :language: python :dedent: 4 :caption: Get a TopicClient for the specified topic. """ try: subscription = self.mgmt_client.get_subscription(topic_name, subscription_name) except requests.exceptions.ConnectionError as e: raise ServiceBusConnectionError("Namespace: {} not found".format(self.service_namespace), e) except AzureServiceBusResourceNotFound: raise ServiceBusResourceNotFound("Specificed subscription does not exist.") return SubscriptionClient.from_entity( self._get_host(), topic_name, subscription, shared_access_key_name=self.shared_access_key_name, shared_access_key_value=self.shared_access_key_value, loop=self.loop, debug=self.debug) def list_subscriptions(self, topic_name): """Get an async client for all subscription entities in the topic. :param topic_name: The topic to list subscriptions for. :type topic_name: str :rtype: list[~azure.servicebus.aio.async_client.SubscriptionClient] :raises: ~azure.servicebus.common.errors.ServiceBusConnectionError if the namespace is not found. :raises: ~azure.servicebus.common.errors.ServiceBusResourceNotFound if the topic is not found. """ try: subs = self.mgmt_client.list_subscriptions(topic_name) except requests.exceptions.ConnectionError as e: raise ServiceBusConnectionError("Namespace: {} not found".format(self.service_namespace), e) except AzureServiceBusResourceNotFound: raise ServiceBusResourceNotFound("Specificed topic does not exist.") sub_clients = [] for sub in subs: sub_clients.append(SubscriptionClient.from_entity( self._get_host(), topic_name, sub, shared_access_key_name=self.shared_access_key_name, shared_access_key_value=self.shared_access_key_value, loop=self.loop, debug=self.debug)) return sub_clients
class AzureQueueManager(QueueManager): '''Class to abstract the queue infrastructure ''' bus_service = None def __init__(): # setop logger in upper class super().__init__() def get_live_servicebus_config(): '''inspired by https://github.com/Azure/azure-sdk-for-python/blob/master/azure-servicebus/conftest.py''' parser = ConfigParser.RawConfigParser() parser.read('queue_account.conf') conf['service_bus_hostname'] = parser.get( 'ServiceBusService_account', 'service_bus_hostname', fallback=os.environ['SERVICE_BUS_HOSTNAME']) conf['service_namespace'] = parser.get( 'ServiceBusService_account', 'service_namespace', fallback=os.environ['SERVICE_BUS_HOSTNAME']) conf['shared_access_key_name'] = parser.get( 'ServiceBusService_account', 'shared_access_key_name', fallback=os.environ['SERVICE_BUS_SAS_POLICY']) conf['shared_access_key_value'] = parser.get( 'ServiceBusService_account', 'shared_access_key_value', fallback=os.environ['SERVICE_BUS_SAS_KEY']) conf['shared_access_connection_str'] = parser.get( 'ServiceBusService_account', 'shared_access_connection_str', fallback=os.environ['SERVICE_BUS_CONNECTION_STR']) return conf def connect(): ''' Setup connection to ServiceBus. Set connection in file: ./queue_account.conf or settign the following env vars: SERVICE_BUS_HOSTNAME # e.g. primare-desa-service-bus SERVICE_BUS_SAS_POLICY # eg. RootManageSharedAccessKey SERVICE_BUS_SAS_KEY # e.g. the key available in azure portal under RootManageSharedAccessKey ''' conf = self.get_live_servicebus_config() try: if self.bus_service: self.logger.warning( "ServiceBusService reset to new credentials") self.bus_service = ServiceBusService( service_namespace=conf['service_namespace'], shared_access_key_name=conf['shared_access_key_name'], shared_access_key_value=conf['shared_access_key_value']) except ValueError: self.bus_service = None self.logger.exception("Exception occurred") def isConnected(): return self.bus_service is not None def topics(): return self.bus_service.list_topics() def subscribe(topic_name, subscribe_name): '''Subscribe to a topic: topic_name and calling subscription as subscribe_name. Subscription have to explicitaly removed to disconnect to a topic. I't configured to avoid throwing exception if topic already exists. TODO: SQL filtering ''' if not topic_name: raise Exception('No topic name set') if not subscribe_name: raise Exception('No named subscription is set') fail_on_exist = False self.bus_service.create_subscription(topic_name, subscribe_name, fail_on_exist) def removeSubscribtion(topic_name, subscribe_name): '''Remove subscription named: subscribe_name to a topic: topic_name. It's configured to avoid throwing exception if topic does not exists. TODO: SQL filtering ''' if not topic_name: raise Exception('No topic name set') if not subscribe_name: raise Exception('No named subscription is set') fail_not_exist = False self.bus_service.delete_subscription(topic_name, subscribe_name, fail_not_exist) def peekMessage(topic_name, subscription_name, timeout=60): '''Peek a message without removing and loking it. Get message from topic: topic_name and subscription as subscribe_name. Subscription have to explicitaly removed to disconnect to a topic. Unlock is done following peek_lock_subscription_message logic. ''' return self.bus_service.peek_lock_subscription_message( topic_name, subscription_name, timeout) def removeMessage(topic_name, subscription_name, identifier={ 'sequence_number': None, 'lock_token': None }): '''Remove a locked message of a topic_name beloging to a subscription_name. Mesage is referred via sequence_number and/or lock_token retrieved during peekMessage/peek_lock_subscription_message ''' self.bus_service.delete_subscription_message( topic_name=topic_name, subscription_name=subscription_name, sequence_number=identifier['sequence_number'], lock_token=identifier['lock_token']) def unlockMessage(topic_name, subscription_name, identifier={ 'sequence_number': None, 'lock_token': None }): '''Remove a locked message of a topic_name beloging to a subscription_name. Mesage is referred via sequence_number and/or lock_token retrieved during peekMessage/peek_lock_subscription_message ''' self.bus_service.unlock_subscription_message( topic_name=topic_name, subscription_name=subscription_name, sequence_number=identifier['sequence_number'], lock_token=identifier['lock_token'])