Example #1
0
    def _init_common(self):
        """
        Common initialization which needs to be done whether an object is
        created via `__init__` or `_init_from_config_file` is done in this
        method.
        """
        self._validate_required_content()

        client_id = UuidGenerator.generate_id_as_string()

        # The number of times to retry during connect
        self._connect_retries = self._DEFAULT_CONNECT_RETRIES
        # The keep alive interval
        self._keep_alive_interval = self._DEFAULT_MQTT_KEEP_ALIVE_INTERVAL
        # The reconnect back off multiplier
        self._reconnect_back_off_multiplier = \
            self._DEFAULT_RECONNECT_BACK_OFF_MULTIPLIER
        # The reconnect delay (in seconds)
        self._reconnect_delay = self._DEFAULT_RECONNECT_DELAY
        # The maximum reconnect delay
        self._reconnect_delay_max = self._DEFAULT_RECONNECT_DELAY_MAX
        # The reconnect delay random
        self._reconnect_delay_random = self._DEFAULT_RECONNECT_DELAY_RANDOM
        # Whether to reconnect when disconnected
        self._reconnect_when_disconnected = \
            self._DEFAULT_RECONNECT_WHEN_DISCONNECTED

        # The unique identifier of the client
        self._client_id = client_id
        # Queue for getting the sorted broker list
        self._queue = None
        # The incoming message queue size
        self._incoming_message_queue_size = 1000
        # The incoming thread pool size
        self._incoming_message_thread_pool_size = 1
    def __init__(self, broker_ca_bundle, cert_file, private_key, brokers):
        """
        Constructor parameters:

        :param broker_ca_bundle: The file name of a bundle containing the broker CA certificates in PEM format
        :param cert_file: The file name of the client certificate in PEM format
        :param private_key: The file name of the client private key in PEM format
        :param brokers: A list of :class:`dxlclient.broker.Broker` objects representing brokers comprising the
            DXL fabric. When invoking the :func:`dxlclient.client.DxlClient.connect` method, the
            :class:`dxlclient.client.DxlClient` will attempt to connect to the closest broker.
        """
        super(DxlClientConfig, self).__init__()

        client_id = UuidGenerator.generate_id_as_string()

        if not broker_ca_bundle:
            raise ValueError("Broker CA bundle not specified")

        if not cert_file:
            raise ValueError("Certificate file not specified")

        if not private_key:
            raise ValueError("Private key file not specified")

        if brokers is None:
            raise ValueError("Brokers were not specified")

        # The number of times to retry during connect
        self._connect_retries = self._DEFAULT_CONNECT_RETRIES
        # The keep alive interval
        self._keep_alive_interval = self._DEFAULT_MQTT_KEEP_ALIVE_INTERVAL
        # The reconnect back off multiplier
        self._reconnect_back_off_multiplier = self._DEFAULT_RECONNECT_BACK_OFF_MULTIPLIER
        # The reconnect delay (in seconds)
        self._reconnect_delay = self._DEFAULT_RECONNECT_DELAY
        # The maximum reconnect delay
        self._reconnect_delay_max = self._DEFAULT_RECONNECT_DELAY_MAX
        # The reconnect delay random
        self._reconnect_delay_random = self._DEFAULT_RECONNECT_DELAY_RANDOM
        # Whether to reconnect when disconnected
        self._reconnect_when_disconnected = self._DEFAULT_RECONNECT_WHEN_DISCONNECTED

        # The unique identifier of the client
        self._client_id = client_id
        # The list of brokers
        self.brokers = brokers
        # The filename of the CA bundle file in PEM format
        self.broker_ca_bundle = broker_ca_bundle
        # The filename of the client certificate in PEM format (must not have a password)
        self.cert_file = cert_file
        # The filename of the private key used to request the certificates
        self.private_key = private_key
        # Queue for getting the sorted broker list
        self._queue = None
        # The incoming message queue size
        self._incoming_message_queue_size = 1000
        # The incoming thread pool size
        self._incoming_message_thread_pool_size = 1
Example #3
0
    def __init__(self, tasks, thread_prefix):
        """
        Constructs a ThreadPoolWorker.
        """
        Thread.__init__(self)

        _ObjectTracker.get_instance().obj_constructed(self)

        self.tasks = tasks
        self.daemon = True
        self.name = thread_prefix + "-" + UuidGenerator.generate_id_as_string()
        self.start()
Example #4
0
    def __init__(self, client, service_type):
        """
        Constructor parameters:
        
        :param client: The :class:`dxlclient.client.DxlClient` instance that will expose this service
        :param service_type: A textual name for the service. For example, "/mycompany/myservice"
        """
        super(ServiceRegistrationInfo, self).__init__()

        # if not isinstance(channels, list):
        # raise ValueError('Channels should be a list')
        # if not channels:
        #     raise InvalidServiceException('Channel list is empty')
        if not service_type:
            raise ValueError("Undefined service name")

            #The service type or name prefix
        self._service_type = service_type
        # The unique service ID
        self._service_id = UuidGenerator.generate_id_as_string()

        # The map of registered channels and their associated callbacks
        self._callbacks_by_topic = {}
        #The map of meta data associated with this service (name-value pairs)
        self._metadata = {}
        # List of destination tenants
        self._destination_tenant_guids = []

        # The Time-To-Live (TTL) of the service registration (default: 60 minutes)
        self._ttl = 60  # minutes
        # The minimum Time-To-Live (TTL) of the service registration (default: 10 minutes)
        self._ttl_lower_limit = 10

        # Internal client reference
        self._dxl_client = client

        # Registration sync object
        self._registration_sync = Condition()

        # Whether at least one registration has occurred
        self._registration_occurred = False

        # Whether at least one unregistration registration has occurred
        self._unregistration_occurred = False

        self._destroy_lock = RLock()
        self._destroyed = False
Example #5
0
    def __init__(self, destination_topic):
        """
        Constructor parameters:

        :param destination_topic: The topic to publish the message to
        """
        super(Message, self).__init__()

        ###########
        # Version 0
        ###########
        # The version of the message
        self._version = self.MESSAGE_VERSION
        # The unique identifier for the message
        self._message_id = UuidGenerator.generate_id_as_string()
        # The identifier for the client that is the source of the message
        self._source_client_id = ""
        # The GUID for the broker that is the source of the message
        self._source_broker_id = ""
        # The channel that the message is published on
        self._destination_topic = destination_topic
        # The payload to send with the message
        self._payload = bytes()
        # The set of broker GUIDs to deliver the message to
        self._broker_ids = []
        # The set of client GUIDs to deliver the message to
        self._client_ids = []

        ###########
        # Version 1
        ###########
        # Other fields: way to add fields to message types
        self._other_fields = {}

        ###########
        # Version 2
        ###########
        # The GUID for the tenant that is the source of the message
        self._source_tenant_guid = ""
        # The set of tenant GUIDs to deliver the message to
        self._destination_tenant_guids = []
    def _update_broker_config_model(self):
        """
        Set the contents of :meth:`brokers` into the configobj model,
        converting the list of :class:`dxlclient.broker.Broker` objects into a
        `dict` matching the format needed for the dxlclient config file.
        """
        brokers = self.brokers
        if brokers is None:
            self._set_value_to_config(self._BROKERS_SECTION, None)
        else:
            brokers_for_config = OrderedDict()
            # A `unique_id` is not required for the in-memory representation of
            # a `Broker` object but it effectively is required when persisting
            # the broker configuration to a file. If no `unique_id` is found
            # on the `Broker` object, assign a random one as the key for the
            # broker config section so that the file can at least be persisted.
            for broker in brokers:
                unique_id = broker.unique_id if broker.unique_id else \
                    UuidGenerator.generate_id_as_string()
                brokers_for_config[unique_id] = broker._to_broker_string()

            # In order to attempt to preserve any comments that may have been
            # added to the configuration file for a pre-existing broker, this
            # code preserves the `configobj` model objects which already
            # exist for the brokers being set.
            current_brokers = self._get_value_from_config(
                self._BROKERS_SECTION)
            brokers_to_delete = []
            for current_broker_key in current_brokers.keys():
                if current_broker_key not in brokers_for_config:
                    brokers_to_delete.append(current_broker_key)

            # `configobj` model entries not present in the brokers to be set
            # are deleted - along with any associated comments.
            for broker_to_delete in brokers_to_delete:
                del current_brokers[broker_to_delete]

            # The `merge` updates the values for pre-existing keys and adds in
            # new key/value pairs which are not already present in the config
            # model.
            current_brokers.merge(brokers_for_config)
Example #7
0
    def parse(broker_url):
        """
        Returns a broker instance corresponding to the specified broker URL of the form:

        ``[ssl://]<hostname>[:port]``

        Valid URLs include:

        - ``ssl://mybroker:8883``
        - ``ssl://mybroker``
        - ``mybroker:8883``
        - ``mybroker``

        If the port is omitted it will be defaulted to 8883.

        :param broker_url: A valid broker URL
        :return: A broker corresponding to the specified broker URL
        """
        broker = Broker(host_name='none')
        elements = broker_url.split("://")
        host_name = broker_url
        protocol = Broker._SSL_PROTOCOL
        port = Broker._SSL_PORT
        if len(elements) == 2:
            protocol = elements[0]
            host_name = elements[1]
        if host_name[-1] != ']':
            host_name_left, _, host_name_right = host_name.rpartition(":")
            if host_name_left:
                host_name = host_name_left
                port = host_name_right
        broker.host_name = host_name
        broker.port = port
        broker.unique_id = UuidGenerator.generate_id_as_string()

        if protocol and protocol.lower() != Broker._SSL_PROTOCOL.lower():
            raise MalformedBrokerUriException("Unknown protocol: " + protocol)

        return broker
Example #8
0
    def __init__(self, config):
        """
        Constructor parameters:

        :param config: The :class:`dxlclient.client_config.DxlClientConfig` object containing the configuration
            settings for the client.
        """
        super(DxlClient, self).__init__()

        if config is None or not isinstance(config, DxlClientConfig):
            raise ValueError("Client configuration not specified")

        # The client configuration
        self._config = config
        # The lock for the client configuration
        self._config_lock = threading.RLock()
        # The condition associated with the client configuration
        self._config_lock_condition = threading.Condition(self._config_lock)

        # The flag for the connection state
        self._connected = False
        # The lock for the flag for the connection state
        self._connected_lock = threading.RLock()
        # The condition for the flag on connection state
        self._connected_wait_condition = threading.Condition(
            self._connected_lock)
        # The current broker the client is connected to
        self._current_broker = None
        # The lock for the current broker the client is connected to
        self._current_broker_lock = threading.RLock()

        # The default wait time for a synchronous request
        self._default_wait = self._DEFAULT_WAIT

        # The wait for policy delay (in seconds)
        self._wait_for_policy_delay = self._DEFAULT_WAIT_FOR_POLICY_DELAY

        # The minimum amount of threads in a thread pool
        self._core_pool_size = self._DEFAULT_MIN_POOL_SIZE
        # The maximum amount of threads in a thread pool
        self._maximum_pool_size = self._DEFAULT_MAX_POOL_SIZE

        # The quality of server (QOS) for messages
        self._qos = self._DEFAULT_QOS
        # The "reply-to" prefix. self is typically used for setting up response
        # channels for requests, etc.
        self._reply_to_topic = self._REPLY_TO_PREFIX + self._config._client_id

        # The request callbacks manager
        self._request_callbacks = callback_manager._RequestCallbackManager()
        # The response callbacks manager
        self._response_callbacks = callback_manager._ResponseCallbackManager()
        # The event callbacks manager
        self._event_callbacks = callback_manager._EventCallbackManager()

        # The current list of subscriptions
        self._subscriptions = set()
        # The lock for the current list of subscriptions
        self._subscriptions_lock = threading.RLock()

        # The underlying MQTT client instance
        self._client = mqtt.Client(client_id=self._config._client_id,
                                   clean_session=True,
                                   userdata=self,
                                   protocol=mqtt.MQTTv31)

        # The MQTT client connect callback
        self._client.on_connect = _on_connect
        # The MQTT client disconnect callback
        self._client.on_disconnect = _on_disconnect
        # The MQTT client message callback
        self._client.on_message = _on_message
        # The MQTT client log callback
        if logger.isEnabledFor(logging.DEBUG):
            self._client.on_log = _on_log

        # pylint: disable=no-member
        # The MQTT client TLS configuration
        self._client.tls_set(config.broker_ca_bundle,
                             certfile=config.cert_file,
                             keyfile=config.private_key,
                             cert_reqs=ssl.CERT_REQUIRED,
                             tls_version=ssl.PROTOCOL_SSLv23,
                             ciphers=None)
        # The MQTT client TLS configuration to bypass hostname validation
        self._client.tls_insecure_set(True)

        # Generate a message pool prefix
        self._message_pool_prefix = "DxlMessagePool-" + UuidGenerator.generate_id_as_string(
        )

        # The thread pool for message handling
        self._thread_pool = ThreadPool(
            num_threads=config.incoming_message_thread_pool_size,
            queue_size=config.incoming_message_queue_size,
            thread_prefix=self._message_pool_prefix)

        # Subscribe to the client reply channel
        self.subscribe(self._reply_to_topic)

        # The request manager (manages synchronous and asynchronous request callbacks,
        # notifications, etc.).
        self._request_manager = RequestManager(client=self)

        # The service manager (manages services request callbacks, notifications, etc.).
        self._service_manager = _ServiceManager(client=self)

        # The loop thread
        self._thread = None
        # The loop thread terminate flag
        self._thread_terminate = False

        # The lock for the connect thread
        self._connect_wait_lock = threading.RLock()
        # The condition associated with the client configuration
        self._connect_wait_condition = threading.Condition(
            self._connect_wait_lock)

        self._destroy_lock = threading.RLock()
        self._destroyed = False