0
votes

Having some really weird issues with my Python Script, but I was able to replicate it using Microsoft's sample script included in their Python SDK here.

Basically, when I run this code using anything but MQTT_WS, it fails with various errors. Here is the code: (Note, I removed the actual cert/keys, so this will not work for you).

# Copyright (c) Microsoft. All rights reserved.
# Licensed under the MIT license. See LICENSE file in the project root for
# full license information.

import random
import time
import sys
import iothub_client
from iothub_client import IoTHubClient, IoTHubClientError, IoTHubTransportProvider, IoTHubClientResult
from iothub_client import IoTHubMessage, IoTHubMessageDispositionResult, IoTHubError
from iothub_client_args import get_iothub_opt, OptionError

# HTTP options
# Because it can poll "after 9 seconds" polls will happen effectively
# at ~10 seconds.
# Note that for scalabilty, the default value of minimumPollingTime
# is 25 minutes. For more information, see:
# https://azure.microsoft.com/documentation/articles/iot-hub-devguide/#messaging
TIMEOUT = 241000
MINIMUM_POLLING_TIME = 9

# messageTimeout - the maximum time in milliseconds until a message times out.
# The timeout period starts at IoTHubClient.send_event_async.
# By default, messages do not expire.
MESSAGE_TIMEOUT = 10000

RECEIVE_CONTEXT = 0
AVG_WIND_SPEED = 10.0
MIN_TEMPERATURE = 20.0
MIN_HUMIDITY = 60.0
MESSAGE_COUNT = 5
RECEIVED_COUNT = 0

# global counters
RECEIVE_CALLBACKS = 0
SEND_CALLBACKS = 0

PROTOCOL = IoTHubTransportProvider.MQTT_WS

# String containing Hostname, Device Id in the format:
# "HostName=<host_name>;DeviceId=<device_id>;x509=true"
CONNECTION_STRING = "HostName=hub.azure-devices.net;DeviceId=device;x509=true"

MSG_TXT = "{\"deviceId\": \"device\",\"windSpeed\": %.2f,\"temperature\": %.2f,\"humidity\": %.2f}"

X509_CERTIFICATE = (
    '''-----BEGIN CERTIFICATE-----
MIIFtTCCA52gAwIBAgIBAzANBgkqhkiG9w0BAQsFADA0MTIwMAYDVQQDDClBenVy
...
T4kySzeQghCYqpkF06hER0KXTP8shMZedg==
-----END CERTIFICATE-----'''

)

X509_PRIVATEKEY = (
    '''-----BEGIN RSA PRIVATE KEY-----
MIIJKAIBAAKCAgEAnEOzMmctBZke/1kD+5g/soUTucJ28odMTW2RIlfj4kAhT1gW
...
UOBMkQUpHEBNWtBbQIyJbgrb26P1oec7dk5f2xvg7tHjSZLDOZprdEp8gxU=
-----END RSA PRIVATE KEY-----'''
)


# some embedded platforms need certificate information


def receive_message_callback(message, counter):
    global RECEIVE_CALLBACKS
    message_buffer = message.get_bytearray()
    size = len(message_buffer)
    print("Received Message [%d]:" % counter)
    print("    Data: <<<%s>>> & Size=%d" % (message_buffer[:size].decode('utf-8'), size))
    map_properties = message.properties()
    key_value_pair = map_properties.get_internals()
    print("    Properties: %s" % key_value_pair)
    counter += 1
    RECEIVE_CALLBACKS += 1
    print("    Total calls received: %d" % RECEIVE_CALLBACKS)
    return IoTHubMessageDispositionResult.ACCEPTED


def send_confirmation_callback(message, result, user_context):
    global SEND_CALLBACKS
    print("Confirmation[%d] received for message with result = %s" % (user_context, result))
    map_properties = message.properties()
    print("    message_id: %s" % message.message_id)
    print("    correlation_id: %s" % message.correlation_id)
    key_value_pair = map_properties.get_internals()
    print("    Properties: %s" % key_value_pair)
    SEND_CALLBACKS += 1
    print("    Total calls confirmed: %d" % SEND_CALLBACKS)


def iothub_client_init():
    # prepare iothub client
    client = IoTHubClient(CONNECTION_STRING, PROTOCOL)

    # HTTP specific settings
    if client.protocol == IoTHubTransportProvider.HTTP:
        client.set_option("timeout", TIMEOUT)
        client.set_option("MinimumPollingTime", MINIMUM_POLLING_TIME)

    # set the time until a message times out
    client.set_option("messageTimeout", MESSAGE_TIMEOUT)

    # this brings in x509 privateKey and certificate
    client.set_option("x509certificate", X509_CERTIFICATE)
    client.set_option("x509privatekey", X509_PRIVATEKEY)

    # to enable MQTT logging set to 1
    if client.protocol == IoTHubTransportProvider.MQTT:
        client.set_option("logtrace", 0)

    client.set_message_callback(
        receive_message_callback, RECEIVE_CONTEXT)
    return client


def print_last_message_time(client):
    try:
        last_message = client.get_last_message_receive_time()
        print("Last Message: %s" % time.asctime(time.localtime(last_message)))
        print("Actual time : %s" % time.asctime())
    except IoTHubClientError as iothub_client_error:
        if iothub_client_error.args[0].result == IoTHubClientResult.INDEFINITE_TIME:
            print("No message received")
        else:
            print(iothub_client_error)


def iothub_client_sample_x509_run():
    try:

        client = iothub_client_init()

        while True:
            # send a few messages every minute
            print("IoTHubClient sending %d messages" % MESSAGE_COUNT)

            for message_counter in range(0, MESSAGE_COUNT):
                temperature = MIN_TEMPERATURE + (random.random() * 10)
                humidity = MIN_HUMIDITY + (random.random() * 20)
                msg_txt_formatted = MSG_TXT % (
                    AVG_WIND_SPEED + (random.random() * 4 + 2),
                    temperature,
                    humidity)
                # messages can be encoded as string or bytearray
                if (message_counter & 1) == 1:
                    message = IoTHubMessage(bytearray(msg_txt_formatted, 'utf8'))
                else:
                    message = IoTHubMessage(msg_txt_formatted)
                # optional: assign ids
                message.message_id = "message_%d" % message_counter
                message.correlation_id = "correlation_%d" % message_counter
                # optional: assign properties
                prop_map = message.properties()
                prop_map.add("temperatureAlert", 'true' if temperature > 28 else 'false')

                client.send_event_async(message, send_confirmation_callback, message_counter)
                print(
                    "IoTHubClient.send_event_async accepted message [%d] for transmission to IoT Hub." % message_counter)

            # Wait for Commands or exit
            print("IoTHubClient waiting for commands, press Ctrl-C to exit")

            status_counter = 0
            while status_counter <= MESSAGE_COUNT:
                status = client.get_send_status()
                print("Send status: %s" % status)
                time.sleep(10)
                status_counter += 1

    except IoTHubError as iothub_error:
        print("Unexpected error %s from IoTHub" % iothub_error)
        return
    except KeyboardInterrupt:
        print("IoTHubClient sample stopped")

    print_last_message_time(client)


def usage():
    print("Usage: iothub_client_sample.py -p <protocol> -c <connectionstring>")
    print("    protocol        : <amqp, http, mqtt>")
    print("    connectionstring: <HostName=<host_name>;DeviceId=<device_id>;SharedAccessKey=<device_key>>")


if __name__ == '__main__':
    print("\nPython %s" % sys.version)
    # print ( "IoT Hub for Python SDK Version: %s" % iothub_client.__version__ )

    try:
        (CONNECTION_STRING, PROTOCOL) = get_iothub_opt(sys.argv[1:], CONNECTION_STRING, PROTOCOL)
    except OptionError as option_error:
        print(option_error)
        usage()
        sys.exit(1)

    print("Starting the IoT Hub Python sample...")
    print("    Protocol %s" % PROTOCOL)
    print("    Connection string=%s" % CONNECTION_STRING)

    iothub_client_sample_x509_run()

SUCCESSFUL RUN: MQTT_WS (Port 443)

IoTHubClient.send_event_async accepted message [0] for transmission to IoT Hub.

Confirmation[0] received for message with result = OK
   message_id: message_0
   correlation_id: correlation_0
   Properties: {'temperatureAlert': 'false'}
   Total calls confirmed: 1

UNSUCCESSFUL RUN: MQTT(Port 8883) - Message Timeout during retrieval

IoTHubClient.send_event_async accepted message [0] for transmission to IoT Hub.

Confirmation[0] received for message with result = MESSAGE_TIMEOUT
   message_id: message_0
   correlation_id: correlation_0
   Properties: {'temperatureAlert': 'false'}
   Total calls confirmed: 1

UNSUCCESSFUL RUN: AMQP(Port 5671) - Errors when trying to connect, then Message Timeout

    IoTHubClient.send_event_async accepted message [0] for transmission to IoT Hub.

Error: Time:Tue Aug 21 17:14:37 2018 File:C:\Release\iot-sdks-internals\release\python\automation\aziotsdk_pytools\src\c\iothub_client\src\iothubtransport_amqp_common.c Func:on_amqp_connection_state_changed Line:772 amqp_connection was closed unexpectedly; connection retry will be triggered.
Info: Preparing transport for re-connection
Error: Time:Tue Aug 21 17:14:37 2018 File:C:\Release\iot-sdks-internals\release\python\automation\aziotsdk_pytools\src\c\iothub_client\src\iothubtransportamqp_methods.c Func:iothubtransportamqp_methods_unsubscribe Line:891 unsubscribe called while not subscribed
Error: Time:Tue Aug 21 17:14:37 2018 File:C:\Release\iot-sdks-internals\release\python\automation\aziotsdk_pytools\src\c\c-utility\adapters\tlsio_schannel.c Func:send_chunk Line:430 invalid tls_io_instance->tlsio_state: TLSIO_STATE_NOT_OPEN
Error: Time:Tue Aug 21 17:14:37 2018 File:C:\Release\iot-sdks-internals\release\python\automation\aziotsdk_pytools\src\c\c-utility\adapters\tlsio_schannel.c Func:internal_send Line:525 send_chunk failed
Error: Time:Tue Aug 21 17:14:37 2018 File:C:\Release\iot-sdks-internals\release\python\automation\aziotsdk_pytools\src\c\c-utility\adapters\tlsio_schannel.c Func:tlsio_schannel_send Line:1263 send failed
Error: Time:Tue Aug 21 17:14:37 2018 File:C:\Release\iot-sdks-internals\release\python\automation\aziotsdk_pytools\src\c\uamqp\src\connection.c Func:on_bytes_encoded Line:268 Cannot send encoded bytes
Error: Time:Tue Aug 21 17:14:37 2018 File:C:\Release\iot-sdks-internals\release\python\automation\aziotsdk_pytools\src\c\c-utility\adapters\tlsio_schannel.c Func:tlsio_schannel_close Line:1195 invalid tls_io_instance->tlsio_state = TLSIO_STATE_NOT_OPEN
Error: Time:Tue Aug 21 17:14:37 2018 File:C:\Release\iot-sdks-internals\release\python\automation\aziotsdk_pytools\src\c\uamqp\src\connection.c Func:on_bytes_encoded Line:272 xio_close failed
Error: Time:Tue Aug 21 17:14:37 2018 File:C:\Release\iot-sdks-internals\release\python\automation\aziotsdk_pytools\src\c\c-utility\adapters\tlsio_schannel.c Func:tlsio_schannel_close Line:1195 invalid tls_io_instance->tlsio_state = TLSIO_STATE_NOT_OPEN
Error: Time:Tue Aug 21 17:14:37 2018 File:C:\Release\iot-sdks-internals\release\python\automation\aziotsdk_pytools\src\c\uamqp\src\connection.c Func:connection_close Line:1437 xio_close failed
Error: Time:Tue Aug 21 17:14:37 2018 File:C:\Release\iot-sdks-internals\release\python\automation\aziotsdk_pytools\src\c\iothub_client\src\iothubtransport_amqp_common.c Func:on_amqp_connection_state_changed Line:772 amqp_connection was closed unexpectedly; connection retry will be triggered.
Info: Preparing transport for re-connection
Confirmation[0] received for message with result = MESSAGE_TIMEOUT
   message_id: message_0
   correlation_id: correlation_0
   Properties: {'temperatureAlert': 'false'}
   Total calls confirmed: 1

UNSUCCESSFUL RUN: AMQP_WS(Port 443) - Errors when trying to connect, then Message Timeout

Same as regular AMQP

UNSUCCESSFUL RUN: HTTP(Port 443) - Errors when trying to connect

IoTHubClient.send_event_async accepted message [0] for transmission to IoT Hub.
Error: Time:Tue Aug 21 17:20:18 2018 File:C:\Release\iot-sdks-internals\release\python\automation\aziotsdk_pytools\src\c\iothub_client\src\iothubtransporthttp.c Func:DoEvent Line:1700 unexpected HTTP status code (401)
Error: Time:Tue Aug 21 17:20:18 2018 File:C:\Release\iot-sdks-internals\release\python\automation\aziotsdk_pytools\src\c\iothub_client\src\iothubtransporthttp.c Func:DoMessages Line:2102 expected status code was 200, but actually was received 401... moving on
IoTHubClient.send_event_async accepted message [4] for transmission to IoT Hub.

Error: Time:Tue Aug 21 17:20:18 2018 File:C:\Release\iot-sdks-internals\release\python\automation\aziotsdk_pytools\src\c\iothub_client\src\iothubtransporthttp.c Func:DoEvent Line:1700 unexpected HTTP status code (401)

I have Python version 3.6.6, on a Windows 10 machine.

Honestly, I'm not sure what is causing this issue. I don't think it's the certificates, because they work when I use them with MQTT_WS. I tried disabling Firewall on Windows for a brief period while I tested and that didn't help either.

Also, this was working before. I had a separate script that I built that would connect up to a protocol that the user passed in, and my test scripts would loop through to ensure it was all working. I took a break to work on getting those scripts to work for Mac/Linux. I ran into road blocks on those platforms, and when I switched back over to Windows, realized that it was running into the same issues. I have not changed anything on the Azure Hub to prevent connections of a certain type, so not sure what is causing this issue.

I've tried generating new certificates and creating a new x509 self signed device, but it was running into the same issues as well :(

Hope someone out there can help!

Edit: I was able to resolve my issue by creating new certificates for IoTHub. It seems the ones I had on there were generated with test scripts, so were only good for 30 days. I do not understand how the MQTT_WS protocol was still able to resolve and get through, though, but that's a separate issue altogether.

Thanks for those who tried to help!

1
Is the issue fix now?Fei Xue - MSFT
Yes, I will provide an edit. Seems that my certificates to my Azure account had expired, but somehow the MQTT_WS protocol was still making it through... not sure why that was, but I was able to resolve this issue.DustNSummers
Glad to hear the issue was fixed. So is the expired certificate still work for MQTT_WS? There may be some delay after the certificate was expired.Fei Xue - MSFT

1 Answers

1
votes

I am trying to reproduce this issue, however failed. All the protocols you mentioned HTTP, MQTT, MQTT_WS, AMQP, AMQP_WS works well for me using the latest version Python SDK.

To narrow down this issue, I suggest that you create a new Azure IoT Hub to see whether the issue is relative to the specific instance of Azure IoT Hub. You can create a free one for the test purpose.

If the issue is not relative to the specific Azure IoT Hub, the issue also maybe relative to the network issue. Please check the port is not block, you may need to confirm with the IT department if you were working in the environment of organization. Or you can test it in a another network environment to confirm this issue.

And here are the test steps maybe also helpful for your reference:

  1. Create a X.509 Self-Signed certificate via PowerShell

    New-SelfSignedCertificate -DnsName "www.fabrikam.com", "www.contoso.com" -CertStoreLocation "cert:\LocalMachine\My"

  2. Export certificate and key using command below

    openssl pkcs12 -in yourpfxfile.pfx -nokeys -out publiccert.pem -nodes

    openssl pkcs12 -in x.pfx -nocerts -nodes -passin pass:123456 | openssl rsa -out privkey.pem

  3. Add a X.509 Self-Signed certificate device on Azure portal

  4. Download the python script from GitHub code sample link
  5. Install the latest Azure IoT Device SDK

    python -m pip install azure-iothub-device-client

  6. Modify the code with the certificate, key and specific protocol