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
Пример #2
0
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'])