From c316ff570237e3aec4cba16eeb6883ff3ebcb6e2 Mon Sep 17 00:00:00 2001 From: Noah Beard Date: Mon, 21 Feb 2022 17:28:30 -0500 Subject: [PATCH 01/10] Simplified command line parsing Simplifies command line parsing and ensures naming consistency between samples and the other AWS IoT SDKs. --- samples/README.md | 24 ++++++++-------- samples/basic_discovery.py | 40 ++++++++++++--------------- samples/command_line_utils.py | 52 +++++++++++++++++++++++++++++++++++ samples/fleetprovisioning.py | 52 ++++++++++++++++------------------- samples/jobs.py | 41 ++++++++++++--------------- samples/pkcs11_pubsub.py | 42 ++++++++++++---------------- samples/pubsub.py | 49 ++++++++++++++------------------- samples/shadow.py | 39 +++++++++++--------------- 8 files changed, 175 insertions(+), 164 deletions(-) create mode 100644 samples/command_line_utils.py diff --git a/samples/README.md b/samples/README.md index 778b86f4..c6aea304 100644 --- a/samples/README.md +++ b/samples/README.md @@ -22,7 +22,7 @@ Source: `samples/pubsub.py` Run the sample like this: ``` sh -python3 pubsub.py --endpoint --root-ca --cert --key +python3 pubsub.py --endpoint --ca_file --cert --key ``` Your Thing's @@ -119,7 +119,7 @@ To run this sample using [SoftHSM2](https://www.opendnssec.org/softhsm/) as the 5) Now you can run the sample: ```sh - python3 pkcs11_pubsub.py --endpoint --root-ca --cert --pkcs11-lib --pin --token-label --key-label + python3 pkcs11_pubsub.py --endpoint --ca_file --cert --pkcs11_lib --pin --token_label --key_label ## Shadow @@ -146,7 +146,7 @@ Source: `samples/shadow.py` Run the sample like this: ``` sh -python3 shadow.py --endpoint --root-ca --cert --key --thing-name +python3 shadow.py --endpoint --ca_file --cert --key --thing-name ``` Your Thing's @@ -232,7 +232,7 @@ Source: `samples/jobs.py` Run the sample like this: ``` sh -python3 jobs.py --endpoint --root-ca --cert --key --thing-name +python3 jobs.py --endpoint --ca_file --cert --key --thing_name ``` Your Thing's @@ -304,12 +304,12 @@ Source: `samples/fleetprovisioning.py` Run the sample using createKeysAndCertificate: ``` sh -python3 fleetprovisioning.py --endpoint --root-ca --cert --key --templateName --templateParameters +python3 fleetprovisioning.py --endpoint --ca_file --cert --key --template_name --template_parameters ``` Run the sample using createCertificateFromCsr: ``` sh -python3 fleetprovisioning.py --endpoint --root-ca --cert --key --templateName --templateParameters --csr +python3 fleetprovisioning.py --endpoint --ca_file --cert --key --template_name --template_parameters --csr ``` Your Thing's @@ -425,11 +425,11 @@ and `--key` appropriately: ``` sh python3 fleetprovisioning.py \ --endpoint [your endpoint]-ats.iot.[region].amazonaws.com \ - --root-ca [pathToRootCA] \ + --ca_file [pathToRootCA] \ --cert /tmp/provision.cert.pem \ --key /tmp/provision.private.key \ - --templateName [TemplateName] \ - --templateParameters "{\"SerialNumber\":\"1\",\"DeviceLocation\":\"Seattle\"}" + --template_name [TemplateName] \ + --template_parameters "{\"SerialNumber\":\"1\",\"DeviceLocation\":\"Seattle\"}" ``` Notice that we provided substitution values for the two parameters in the template body, `DeviceLocation` and `SerialNumber`. @@ -465,11 +465,11 @@ using a permanent certificate set, replace the paths specified in the `--cert` a ``` sh python3 fleetprovisioning.py \ --endpoint [your endpoint]-ats.iot.[region].amazonaws.com \ - --root-ca [pathToRootCA] \ + --ca_file [pathToRootCA] \ --cert /tmp/provision.cert.pem \ --key /tmp/provision.private.key \ - --templateName [TemplateName] \ - --templateParameters "{\"SerialNumber\":\"1\",\"DeviceLocation\":\"Seattle\"}" \ + --template_name [TemplateName] \ + --template_parameters "{\"SerialNumber\":\"1\",\"DeviceLocation\":\"Seattle\"}" \ --csr /tmp/deviceCert.csr ``` diff --git a/samples/basic_discovery.py b/samples/basic_discovery.py index 001e7cbb..b75cc81a 100644 --- a/samples/basic_discovery.py +++ b/samples/basic_discovery.py @@ -14,23 +14,19 @@ allowed_actions = ['both', 'publish', 'subscribe'] -parser = argparse.ArgumentParser() -parser.add_argument('-r', '--root-ca', action='store', dest='root_ca_path', help='Root CA file path') -parser.add_argument('-c', '--cert', action='store', required=True, dest='certificate_path', help='Certificate file path') -parser.add_argument('-k', '--key', action='store', required=True, dest='private_key_path', help='Private key file path') -parser.add_argument('-n', '--thing-name', action='store', required=True, dest='thing_name', help='Targeted thing name') -parser.add_argument('-t', '--topic', action='store', dest='topic', default='test/topic', help='Targeted topic') -parser.add_argument('-m', '--mode', action='store', dest='mode', default='both', - help='Operation modes: %s'%str(allowed_actions)) -parser.add_argument('-M', '--message', action='store', dest='message', default='Hello World!', - help='Message to publish') -parser.add_argument('--region', action='store', dest='region', default='us-east-1') -parser.add_argument('--max-pub-ops', action='store', dest='max_pub_ops', default=10) -parser.add_argument('--print-discover-resp-only', action='store_true', dest='print_discover_resp_only', default=False) -parser.add_argument('-v', '--verbosity', choices=[x.name for x in LogLevel], default=LogLevel.NoLogs.name, - help='Logging level') - -args = parser.parse_args() +# Parse arguments +import command_line_utils; +cmdUtils = command_line_utils.CommandLineUtils("Basic Discovery - Greengrass discovery example.") +cmdUtils.add_common_mqtt_commands() +cmdUtils.register_command("topic", "", "Topic to publish, subscribe to (optional, default='test/topic').", default="test/topic") +cmdUtils.register_command("thing_name", "", "The name assigned to your IoT Thing", required=True) +cmdUtils.register_command("mode", "", "The operation mode (optional, default='both').\nModes:%s"%str(allowed_actions), default='both') +cmdUtils.register_command("message", "", "The message to send in the payload (optional, default='Hello World!').", default="Hello World!") +cmdUtils.register_command("region", "", "The region to connect through (optional, default='us-east-1').", default="us-east-1") +cmdUtils.register_command("max_pub_ops", "", "The maximum number of publish operations (optional, default='10').", default=10, type=int) +cmdUtils.register_command("print_discover_resp_only", "", "(optional, default='False').", default=False, type=bool) +cmdUtils.register_command("verbosity", "", "Logging level.", default=io.LogLevel.NoLogs.name, choices=[x.name for x in io.LogLevel]) +args = cmdUtils.get_args() io.init_logging(getattr(LogLevel, args.verbosity), 'stderr') @@ -38,9 +34,9 @@ host_resolver = io.DefaultHostResolver(event_loop_group) client_bootstrap = io.ClientBootstrap(event_loop_group, host_resolver) -tls_options = io.TlsContextOptions.create_client_with_mtls_from_path(args.certificate_path, args.private_key_path) -if args.root_ca_path: - tls_options.override_default_trust_store_from_path(None, args.root_ca_path) +tls_options = io.TlsContextOptions.create_client_with_mtls_from_path(args.cert, args.key) +if args.ca_file: + tls_options.override_default_trust_store_from_path(None, args.ca_file) tls_context = io.ClientTlsContext(tls_options) socket_options = io.SocketOptions() @@ -73,8 +69,8 @@ def try_iot_endpoints(): mqtt_connection = mqtt_connection_builder.mtls_from_path( endpoint=connectivity_info.host_address, port=connectivity_info.port, - cert_filepath=args.certificate_path, - pri_key_filepath=args.private_key_path, + cert_filepath=args.cert, + pri_key_filepath=args.key, client_bootstrap=client_bootstrap, ca_bytes=gg_group.certificate_authorities[0].encode('utf-8'), on_connection_interrupted=on_connection_interupted, diff --git a/samples/command_line_utils.py b/samples/command_line_utils.py new file mode 100644 index 00000000..34fc47d8 --- /dev/null +++ b/samples/command_line_utils.py @@ -0,0 +1,52 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0. + +import argparse +from awscrt import io + +class CommandLineUtils: + def __init__(self, description) -> None: + self.parser = argparse.ArgumentParser(description="Send and receive messages through and MQTT connection.") + self.commands = {} + + def register_command(self, command_name, example_input, help_output, required=False, type=None, default=None, choices=None): + self.commands[command_name] = { + "name":command_name, + "example_input":example_input, + "help_output":help_output, + "required": required, + "type": type, + "default": default, + "choices": choices + } + + def remove_command(self, command_name): + if command_name in self.commands.keys(): + self.commands.pop(command_name) + + def get_args(self): + # add all the commands + for command in self.commands.values(): + self.parser.add_argument("--" + command["name"], metavar=command["example_input"], help=command["help_output"], + required=command["required"], type=command["type"], default=command["default"], choices=command["choices"]) + + return self.parser.parse_args() + + def update_command(self, command_name, new_example_input=None, new_help_output=None, new_required=None, new_type=None, new_default=None): + if command_name in self.commands.keys(): + if new_example_input: + self.commands[command_name]["example_input"] = new_example_input + if new_help_output: + self.commands[command_name]["help_output"] = new_help_output + if new_required: + self.commands[command_name]["required"] = new_required + if new_type: + self.commands[command_name]["type"] = new_type + if new_default: + self.commands[command_name]["default"] = new_default + + def add_common_mqtt_commands(self): + self.register_command("endpoint", "", "The endpoint of the mqtt server not including a port.", True, str) + self.register_command("key", "", "Path to your key in PEM format.", False, str) + self.register_command("cert", "", "Path to your client certificate in PEM format.", False, str) + self.register_command("ca_file", "", "Path to AmazonRootCA1.pem (optional, system trust store used by default)", False, str) diff --git a/samples/fleetprovisioning.py b/samples/fleetprovisioning.py index 1384f4bd..a5e0fcfa 100644 --- a/samples/fleetprovisioning.py +++ b/samples/fleetprovisioning.py @@ -26,31 +26,25 @@ # On startup, the script subscribes to topics based on the request type of either CSR or Keys # publishes the request to corresponding topic and calls RegisterThing. -parser = argparse.ArgumentParser(description="Fleet Provisioning sample script.") -parser.add_argument('--endpoint', required=True, help="Your AWS IoT custom endpoint, not including a port. " + - "Ex: \"w6zbse3vjd5b4p-ats.iot.us-west-2.amazonaws.com\"") -parser.add_argument('--cert', help="File path to your client certificate, in PEM format") -parser.add_argument('--key', help="File path to your private key file, in PEM format") -parser.add_argument('--root-ca', help="File path to root certificate authority, in PEM format. " + - "Necessary if MQTT server uses a certificate that's not already in " + - "your trust store") -parser.add_argument('--client-id', default="test-" + str(uuid4()), help="Client ID for MQTT connection.") -parser.add_argument('--use-websocket', default=False, action='store_true', - help="To use a websocket instead of raw mqtt. If you " + - "specify this option you must specify a region for signing.") -parser.add_argument('--signing-region', default='us-east-1', help="If you specify --use-web-socket, this " + - "is the region that will be used for computing the Sigv4 signature") -parser.add_argument('--proxy-host', help="Hostname of proxy to connect to.") -parser.add_argument('--proxy-port', type=int, default=8080, help="Port of proxy to connect to.") -parser.add_argument('--verbosity', choices=[x.name for x in io.LogLevel], default=io.LogLevel.NoLogs.name, - help='Logging level') -parser.add_argument("--csr", help="File path to your client CSR in PEM format") -parser.add_argument("--templateName", help="Template name") -parser.add_argument("--templateParameters", help="Values for Template Parameters") +# Parse arguments +import command_line_utils; +cmdUtils = command_line_utils.CommandLineUtils("Fleet Provisioning - Provision device using either the keys or CSR.") +cmdUtils.add_common_mqtt_commands() +cmdUtils.register_command("client_id", "", "Client ID to use for MQTT connection (optional, default='test-*').", default="test-" + str(uuid4())) +cmdUtils.register_command("use_websocket", "", "If specified, uses a websocket over https (optional).", default=False) +cmdUtils.register_command("signing_region", "", + "Used for websocket signer. It should only be specified if websockets are used (optional, default='us-east-1')", default="us-east-1") +cmdUtils.register_command("proxy_host", "", "Host name of the http proxy to use (optional)") +cmdUtils.register_command("proxy_port", "", "Port of the http proxy to use (optional, default='8080')", type=int, default=8080) +cmdUtils.register_command("verbosity", "", "Logging level.", default=io.LogLevel.NoLogs.name, choices=[x.name for x in io.LogLevel]) +cmdUtils.register_command("csr", "", "Path to CSR in Pem format (optional).") +cmdUtils.register_command("template_name", "", "The name of your provisioning template.") +cmdUtils.register_command("template_parameters", "", "Template parameters json.") +cmdUtils.update_command("cert", new_help_output="Path to your certificate in PEM format. If this is not set you must specify use_websocket", new_required=False) +args = cmdUtils.get_args() # Using globals to simplify sample code is_sample_done = threading.Event() -args = parser.parse_args() io.init_logging(getattr(io.LogLevel, args.verbosity), 'stderr') mqtt_connection = None @@ -246,7 +240,7 @@ def waitForRegisterThingResponse(): http_proxy_options=proxy_options, on_connection_interrupted=on_connection_interrupted, on_connection_resumed=on_connection_resumed, - ca_filepath=args.root_ca, + ca_filepath=args.ca_file, client_id=args.client_id, clean_session=False, keep_alive_secs=30) @@ -257,7 +251,7 @@ def waitForRegisterThingResponse(): cert_filepath=args.cert, pri_key_filepath=args.key, client_bootstrap=client_bootstrap, - ca_filepath=args.root_ca, + ca_filepath=args.ca_file, client_id=args.client_id, on_connection_interrupted=on_connection_interrupted, on_connection_resumed=on_connection_resumed, @@ -328,7 +322,7 @@ def waitForRegisterThingResponse(): createcertificatefromcsr_subscribed_rejected_future.result() - registerthing_subscription_request = iotidentity.RegisterThingSubscriptionRequest(template_name=args.templateName) + registerthing_subscription_request = iotidentity.RegisterThingSubscriptionRequest(template_name=args.template_name) print("Subscribing to RegisterThing Accepted topic...") registerthing_subscribed_accepted_future, _ = identity_client.subscribe_to_register_thing_accepted( @@ -359,9 +353,9 @@ def waitForRegisterThingResponse(): raise Exception('CreateKeysAndCertificate API did not succeed') registerThingRequest = iotidentity.RegisterThingRequest( - template_name=args.templateName, + template_name=args.template_name, certificate_ownership_token=createKeysAndCertificateResponse.certificate_ownership_token, - parameters=json.loads(args.templateParameters)) + parameters=json.loads(args.template_parameters)) else: print("Publishing to CreateCertificateFromCsr...") csrPath = open(args.csr, 'r').read() @@ -376,9 +370,9 @@ def waitForRegisterThingResponse(): raise Exception('CreateCertificateFromCsr API did not succeed') registerThingRequest = iotidentity.RegisterThingRequest( - template_name=args.templateName, + template_name=args.template_name, certificate_ownership_token=createCertificateFromCsrResponse.certificate_ownership_token, - parameters=json.loads(args.templateParameters)) + parameters=json.loads(args.template_parameters)) print("Publishing to RegisterThing topic...") registerthing_publish_future = identity_client.publish_register_thing(registerThingRequest, mqtt.QoS.AT_LEAST_ONCE) diff --git a/samples/jobs.py b/samples/jobs.py index 8639cd1e..aad6afc7 100644 --- a/samples/jobs.py +++ b/samples/jobs.py @@ -33,30 +33,24 @@ # This event is sent by the service when the current job completes, so the # sample will be continually prompted to try another job until none remain. -parser = argparse.ArgumentParser(description="Jobs sample runs all pending job executions.") -parser.add_argument('--endpoint', required=True, help="Your AWS IoT custom endpoint, not including a port. " + - "Ex: \"w6zbse3vjd5b4p-ats.iot.us-west-2.amazonaws.com\"") -parser.add_argument('--cert', help="File path to your client certificate, in PEM format") -parser.add_argument('--key', help="File path to your private key file, in PEM format") -parser.add_argument('--root-ca', help="File path to root certificate authority, in PEM format. " + - "Necessary if MQTT server uses a certificate that's not already in " + - "your trust store") -parser.add_argument('--client-id', default="test-" + str(uuid4()), help="Client ID for MQTT connection.") -parser.add_argument('--thing-name', required=True, help="The name assigned to your IoT Thing") -parser.add_argument('--job-time', default=5, type=float, help="Emulate working on job by sleeping this many seconds.") -parser.add_argument('--use-websocket', default=False, action='store_true', - help="To use a websocket instead of raw mqtt. If you " + - "specify this option you must specify a region for signing.") -parser.add_argument('--signing-region', default='us-east-1', help="If you specify --use-web-socket, this " + - "is the region that will be used for computing the Sigv4 signature") -parser.add_argument('--proxy-host', help="Hostname of proxy to connect to.") -parser.add_argument('--proxy-port', type=int, default=8080, help="Port of proxy to connect to.") -parser.add_argument('--verbosity', choices=[x.name for x in io.LogLevel], default=io.LogLevel.NoLogs.name, - help='Logging level') - # Using globals to simplify sample code is_sample_done = threading.Event() +# Parse arguments +import command_line_utils; +cmdUtils = command_line_utils.CommandLineUtils("Jobs - Recieve and execute operations on the device.") +cmdUtils.add_common_mqtt_commands() +cmdUtils.register_command("client_id", "", "Client ID to use for MQTT connection (optional, default='test-*').", default="test-" + str(uuid4())) +cmdUtils.register_command("thing_name", "", "The name assigned to your IoT Thing", required=True) +cmdUtils.register_command("job_time", "", "Emulate working on a job by sleeping this many seconds (optional, default='5')", default=5) +cmdUtils.register_command("use_websocket", "", "If specified, uses a websocket over https (optional).", default=False) +cmdUtils.register_command("signing_region", "", + "Used for websocket signer. It should only be specified if websockets are used (optional, default='us-east-1')", default="us-east-1") +cmdUtils.register_command("proxy_host", "", "Host name of the http proxy to use (optional)") +cmdUtils.register_command("proxy_port", "", "Port of the http proxy to use (optional, default='8080')", type=int, default=8080) +cmdUtils.register_command("verbosity", "", "Logging level.", default=io.LogLevel.NoLogs.name, choices=[x.name for x in io.LogLevel]) +args = cmdUtils.get_args() + mqtt_connection = None jobs_client = None thing_name = "" @@ -220,7 +214,6 @@ def on_update_job_execution_rejected(rejected): if __name__ == '__main__': # Process input args - args = parser.parse_args() thing_name = args.thing_name io.init_logging(getattr(io.LogLevel, args.verbosity), 'stderr') @@ -241,7 +234,7 @@ def on_update_job_execution_rejected(rejected): region=args.signing_region, credentials_provider=credentials_provider, http_proxy_options=proxy_options, - ca_filepath=args.root_ca, + ca_filepath=args.ca_file, client_id=args.client_id, clean_session=False, keep_alive_secs=30) @@ -252,7 +245,7 @@ def on_update_job_execution_rejected(rejected): cert_filepath=args.cert, pri_key_filepath=args.key, client_bootstrap=client_bootstrap, - ca_filepath=args.root_ca, + ca_filepath=args.ca_file, client_id=args.client_id, clean_session=False, keep_alive_secs=30, diff --git a/samples/pkcs11_pubsub.py b/samples/pkcs11_pubsub.py index 7caeb7cf..1d37f1ba 100644 --- a/samples/pkcs11_pubsub.py +++ b/samples/pkcs11_pubsub.py @@ -19,30 +19,22 @@ # # WARNING: Unix only. Currently, TLS integration with PKCS#11 is only available on Unix devices. -parser = argparse.ArgumentParser(description="Send and receive messages through and MQTT connection.") -parser.add_argument('--endpoint', required=True, help="Your AWS IoT custom endpoint, not including a port. " + - "Ex: \"abcd123456wxyz-ats.iot.us-east-1.amazonaws.com\"") -parser.add_argument('--port', type=int, help="Specify port. AWS IoT supports 443 and 8883. (default: auto)") -parser.add_argument('--cert', required=True, help="File path to your client certificate, in PEM format.") -parser.add_argument('--pkcs11-lib', required=True, help="Path to PKCS#11 library.") -parser.add_argument('--pin', required=True, help="User PIN for logging into PKCS#11 token.") -parser.add_argument('--token-label', help="Label of PKCS#11 token to use. (default: None) ") -parser.add_argument('--slot-id', help="Slot ID containing PKCS#11 token to use. (default: None)") -parser.add_argument('--key-label', help="Label of private key on the PKCS#11 token. (default: None)") -parser.add_argument('--root-ca', help="File path to root certificate authority, in PEM format. (default: None)") -parser.add_argument('--client-id', default="test-" + str(uuid4()), - help="Client ID for MQTT connection. (default: 'test-*')") -parser.add_argument('--topic', default="test/topic", - help="Topic to subscribe to, and publish messages to. (default: 'test/topic')") -parser.add_argument('--message', default="Hello World!", - help="Message to publish. Specify empty string to publish nothing. (default: 'Hello World!')") -parser.add_argument('--count', default=10, type=int, help="Number of messages to publish/receive before exiting. " + - "Specify 0 to run forever. (default: 10)") -parser.add_argument('--verbosity', choices=[x.name for x in io.LogLevel], default=io.LogLevel.NoLogs.name, - help="Logging level. (default: 'NoLogs')") - -# Using globals to simplify sample code -args = parser.parse_args() +# Parse arguments +import command_line_utils; +cmdUtils = command_line_utils.CommandLineUtils("PKCS#11 PubSub - Send and recieve messages through an MQTT connection.") +cmdUtils.add_common_mqtt_commands() +cmdUtils.register_command("port", "", "Connection port. AWS IoT supports 433 and 8883 (optional, default=auto).", type=int) +cmdUtils.register_command("pkcs11_lib", "", "Path to PKCS#11 Library", required=True) +cmdUtils.register_command("pin", "", "User PIN for logging into PKCS#11 token.", required=True) +cmdUtils.register_command("token_label", "", "Label of the PKCS#11 token to use (optional).") +cmdUtils.register_command("slot_id", "", "Slot ID containing the PKCS#11 token to use (optional).") +cmdUtils.register_command("key_label", "", "Label of private key on the PKCS#11 token (optional).") +cmdUtils.register_command("client_id", "", "Client ID to use for MQTT connection (optional, default='test-*').", default="test-" + str(uuid4())) +cmdUtils.register_command("topic", "", "Topic to publish, subscribe to (optional, default='test/topic').", default="test/topic") +cmdUtils.register_command("message", "", "The message to send in the payload (optional, default='Hello World!').", default="Hello World!") +cmdUtils.register_command("count", "", "The number of messages to send (optional, default='10').", default=10, type=int) +cmdUtils.register_command("verbosity", "", "Logging level.", default=io.LogLevel.NoLogs.name, choices=[x.name for x in io.LogLevel]) +args = cmdUtils.get_args() io.init_logging(getattr(io.LogLevel, args.verbosity), 'stderr') @@ -92,7 +84,7 @@ def on_message_received(topic, payload, dup, qos, retain, **kwargs): endpoint=args.endpoint, port=args.port, client_bootstrap=client_bootstrap, - ca_filepath=args.root_ca, + ca_filepath=args.ca_file, on_connection_interrupted=on_connection_interrupted, on_connection_resumed=on_connection_resumed, client_id=args.client_id, diff --git a/samples/pubsub.py b/samples/pubsub.py index 4c712435..dd313f73 100644 --- a/samples/pubsub.py +++ b/samples/pubsub.py @@ -2,6 +2,7 @@ # SPDX-License-Identifier: Apache-2.0. import argparse +from distutils import command from awscrt import io, mqtt, auth, http from awsiot import mqtt_connection_builder import sys @@ -16,33 +17,23 @@ # The device should receive those same messages back from the message broker, # since it is subscribed to that same topic. -parser = argparse.ArgumentParser(description="Send and receive messages through and MQTT connection.") -parser.add_argument('--endpoint', required=True, help="Your AWS IoT custom endpoint, not including a port. " + - "Ex: \"abcd123456wxyz-ats.iot.us-east-1.amazonaws.com\"") -parser.add_argument('--port', type=int, help="Specify port. AWS IoT supports 443 and 8883.") -parser.add_argument('--cert', help="File path to your client certificate, in PEM format.") -parser.add_argument('--key', help="File path to your private key, in PEM format.") -parser.add_argument('--root-ca', help="File path to root certificate authority, in PEM format. " + - "Necessary if MQTT server uses a certificate that's not already in " + - "your trust store.") -parser.add_argument('--client-id', default="test-" + str(uuid4()), help="Client ID for MQTT connection.") -parser.add_argument('--topic', default="test/topic", help="Topic to subscribe to, and publish messages to.") -parser.add_argument('--message', default="Hello World!", help="Message to publish. " + - "Specify empty string to publish nothing.") -parser.add_argument('--count', default=10, type=int, help="Number of messages to publish/receive before exiting. " + - "Specify 0 to run forever.") -parser.add_argument('--use-websocket', default=False, action='store_true', - help="To use a websocket instead of raw mqtt. If you " + - "specify this option you must specify a region for signing.") -parser.add_argument('--signing-region', default='us-east-1', help="If you specify --use-web-socket, this " + - "is the region that will be used for computing the Sigv4 signature") -parser.add_argument('--proxy-host', help="Hostname of proxy to connect to.") -parser.add_argument('--proxy-port', type=int, default=8080, help="Port of proxy to connect to.") -parser.add_argument('--verbosity', choices=[x.name for x in io.LogLevel], default=io.LogLevel.NoLogs.name, - help='Logging level') - -# Using globals to simplify sample code -args = parser.parse_args() +# Parse arguments +import command_line_utils; +cmdUtils = command_line_utils.CommandLineUtils("PubSub - Send and recieve messages through an MQTT connection.") +cmdUtils.add_common_mqtt_commands() +cmdUtils.register_command("port", "", "Connection port. AWS IoT supports 433 and 8883 (optional, default=auto).", type=int) +cmdUtils.register_command("client_id", "", "Client ID to use for MQTT connection (optional, default='test-*').", default="test-" + str(uuid4())) +cmdUtils.register_command("topic", "", "Topic to publish, subscribe to (optional, default='test/topic').", default="test/topic") +cmdUtils.register_command("message", "", "The message to send in the payload (optional, default='Hello World!').", default="Hello World!") +cmdUtils.register_command("count", "", "The number of messages to send (optional, default='10').", default=10, type=int) +cmdUtils.register_command("use_websocket", "", "If specified, uses a websocket over https (optional).", default=False) +cmdUtils.register_command("signing_region", "", + "Used for websocket signer. It should only be specified if websockets are used (optional, default='us-east-1')", default="us-east-1") +cmdUtils.register_command("proxy_host", "", "Host name of the http proxy to use (optional)") +cmdUtils.register_command("proxy_port", "", "Port of the http proxy to use (optional, default='8080')", type=int, default=8080) +cmdUtils.register_command("verbosity", "", "Logging level.", default=io.LogLevel.NoLogs.name, choices=[x.name for x in io.LogLevel]) +cmdUtils.update_command("cert", new_help_output="Path to your certificate in PEM format. If this is not set you must specify use_websocket", new_required=False) +args = cmdUtils.get_args() io.init_logging(getattr(io.LogLevel, args.verbosity), 'stderr') @@ -102,7 +93,7 @@ def on_message_received(topic, payload, dup, qos, retain, **kwargs): region=args.signing_region, credentials_provider=credentials_provider, http_proxy_options=proxy_options, - ca_filepath=args.root_ca, + ca_filepath=args.ca_file, on_connection_interrupted=on_connection_interrupted, on_connection_resumed=on_connection_resumed, client_id=args.client_id, @@ -116,7 +107,7 @@ def on_message_received(topic, payload, dup, qos, retain, **kwargs): cert_filepath=args.cert, pri_key_filepath=args.key, client_bootstrap=client_bootstrap, - ca_filepath=args.root_ca, + ca_filepath=args.ca_file, on_connection_interrupted=on_connection_interrupted, on_connection_resumed=on_connection_resumed, client_id=args.client_id, diff --git a/samples/shadow.py b/samples/shadow.py index 4df52bcb..53e738ab 100644 --- a/samples/shadow.py +++ b/samples/shadow.py @@ -30,26 +30,20 @@ # on the device and an update is sent to the server with the new "reported" # value. -parser = argparse.ArgumentParser(description="Device Shadow sample keeps a property in sync across client and server") -parser.add_argument('--endpoint', required=True, help="Your AWS IoT custom endpoint, not including a port. " + - "Ex: \"w6zbse3vjd5b4p-ats.iot.us-west-2.amazonaws.com\"") -parser.add_argument('--cert', help="File path to your client certificate, in PEM format") -parser.add_argument('--key', help="File path to your private key file, in PEM format") -parser.add_argument('--root-ca', help="File path to root certificate authority, in PEM format. " + - "Necessary if MQTT server uses a certificate that's not already in " + - "your trust store") -parser.add_argument('--client-id', default="test-" + str(uuid4()), help="Client ID for MQTT connection.") -parser.add_argument('--thing-name', required=True, help="The name assigned to your IoT Thing") -parser.add_argument('--shadow-property', default="color", help="Name of property in shadow to keep in sync") -parser.add_argument('--use-websocket', default=False, action='store_true', - help="To use a websocket instead of raw mqtt. If you " + - "specify this option you must specify a region for signing.") -parser.add_argument('--signing-region', default='us-east-1', help="If you specify --use-web-socket, this " + - "is the region that will be used for computing the Sigv4 signature") -parser.add_argument('--proxy-host', help="Hostname of proxy to connect to.") -parser.add_argument('--proxy-port', type=int, default=8080, help="Port of proxy to connect to.") -parser.add_argument('--verbosity', choices=[x.name for x in io.LogLevel], default=io.LogLevel.NoLogs.name, - help='Logging level') +# Parse arguments +import command_line_utils; +cmdUtils = command_line_utils.CommandLineUtils("Shadow - Keep a property in sync between device and server.") +cmdUtils.add_common_mqtt_commands() +cmdUtils.register_command("client_id", "", "Client ID to use for MQTT connection (optional, default='test-*').", default="test-" + str(uuid4())) +cmdUtils.register_command("thing_name", "", "The name assigned to your IoT Thing", required=True) +cmdUtils.register_command("shadow_property", "", "The name of the shadow property you want to change (optional, default='color'", default="color") +cmdUtils.register_command("use_websocket", "", "If specified, uses a websocket over https (optional).", default=False) +cmdUtils.register_command("signing_region", "", + "Used for websocket signer. It should only be specified if websockets are used (optional, default='us-east-1')", default="us-east-1") +cmdUtils.register_command("proxy_host", "", "Host name of the http proxy to use (optional)") +cmdUtils.register_command("proxy_port", "", "Port of the http proxy to use (optional, default='8080')", type=int, default=8080) +cmdUtils.register_command("verbosity", "", "Logging level.", default=io.LogLevel.NoLogs.name, choices=[x.name for x in io.LogLevel]) +args = cmdUtils.get_args() # Using globals to simplify sample code is_sample_done = threading.Event() @@ -297,7 +291,6 @@ def user_input_thread_fn(): if __name__ == '__main__': # Process input args - args = parser.parse_args() thing_name = args.thing_name shadow_property = args.shadow_property io.init_logging(getattr(io.LogLevel, args.verbosity), 'stderr') @@ -319,7 +312,7 @@ def user_input_thread_fn(): region=args.signing_region, credentials_provider=credentials_provider, http_proxy_options=proxy_options, - ca_filepath=args.root_ca, + ca_filepath=args.ca_file, client_id=args.client_id, clean_session=True, keep_alive_secs=30) @@ -330,7 +323,7 @@ def user_input_thread_fn(): cert_filepath=args.cert, pri_key_filepath=args.key, client_bootstrap=client_bootstrap, - ca_filepath=args.root_ca, + ca_filepath=args.ca_file, client_id=args.client_id, clean_session=True, keep_alive_secs=30, From 721b99292d6b9d0900889a5b3a46748c1b0e8452 Mon Sep 17 00:00:00 2001 From: Noah Beard Date: Mon, 21 Feb 2022 17:42:45 -0500 Subject: [PATCH 02/10] Fixed tests not working correctly after simplified command line parsing --- codebuild/samples/pubsub-linux.sh | 2 +- samples/basic_discovery.py | 1 + test/test_samples.py | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/codebuild/samples/pubsub-linux.sh b/codebuild/samples/pubsub-linux.sh index 375c7c8d..24d380b9 100755 --- a/codebuild/samples/pubsub-linux.sh +++ b/codebuild/samples/pubsub-linux.sh @@ -12,6 +12,6 @@ echo "Mqtt Direct test" python3 pubsub.py --endpoint $ENDPOINT --key /tmp/privatekey.pem --cert /tmp/certificate.pem echo "Websocket test" -python3 pubsub.py --endpoint $ENDPOINT --use-websocket --signing-region us-east-1 +python3 pubsub.py --endpoint $ENDPOINT --use_websocket --signing_region us-east-1 popd diff --git a/samples/basic_discovery.py b/samples/basic_discovery.py index b75cc81a..348b6e3c 100644 --- a/samples/basic_discovery.py +++ b/samples/basic_discovery.py @@ -18,6 +18,7 @@ import command_line_utils; cmdUtils = command_line_utils.CommandLineUtils("Basic Discovery - Greengrass discovery example.") cmdUtils.add_common_mqtt_commands() +cmdUtils.remove_command("endpoint") cmdUtils.register_command("topic", "", "Topic to publish, subscribe to (optional, default='test/topic').", default="test/topic") cmdUtils.register_command("thing_name", "", "The name assigned to your IoT Thing", required=True) cmdUtils.register_command("mode", "", "The operation mode (optional, default='both').\nModes:%s"%str(allowed_actions), default='both') diff --git a/test/test_samples.py b/test/test_samples.py index eac74442..2c7756ac 100644 --- a/test/test_samples.py +++ b/test/test_samples.py @@ -94,7 +94,7 @@ def test_pubsub(self): "--endpoint", config.endpoint, "--cert", config.cert_filepath, "--key", config.key_filepath, - "--client-id", create_client_id(), + "--client_id", create_client_id(), "--count", "1", "--verbosity", "Trace", ] @@ -111,11 +111,11 @@ def test_basic_discovery_response_only(self): args = [ sys.executable, "samples/basic_discovery.py", - "--print-discover-resp-only", + "--print_discover_resp_only", "--region", config.region, "--cert", config.cert_filepath, "--key", config.key_filepath, - "--thing-name", "aws-sdk-crt-unit-test", + "--thing_name", "aws-sdk-crt-unit-test", "--verbosity", "Trace", ] From bea34e752e162fa79b141b8547f793009bddd830 Mon Sep 17 00:00:00 2001 From: Noah Beard Date: Tue, 22 Feb 2022 09:37:08 -0500 Subject: [PATCH 03/10] Added action support to command line parser Fixes the backwards compatibility issues the tests were having when trying to run on the CI. --- samples/basic_discovery.py | 2 +- samples/command_line_utils.py | 17 ++++++++++++----- samples/fleetprovisioning.py | 4 ++-- samples/jobs.py | 2 +- samples/pubsub.py | 4 ++-- samples/shadow.py | 2 +- 6 files changed, 19 insertions(+), 12 deletions(-) diff --git a/samples/basic_discovery.py b/samples/basic_discovery.py index 348b6e3c..7f91c7fe 100644 --- a/samples/basic_discovery.py +++ b/samples/basic_discovery.py @@ -25,7 +25,7 @@ cmdUtils.register_command("message", "", "The message to send in the payload (optional, default='Hello World!').", default="Hello World!") cmdUtils.register_command("region", "", "The region to connect through (optional, default='us-east-1').", default="us-east-1") cmdUtils.register_command("max_pub_ops", "", "The maximum number of publish operations (optional, default='10').", default=10, type=int) -cmdUtils.register_command("print_discover_resp_only", "", "(optional, default='False').", default=False, type=bool) +cmdUtils.register_command("print_discover_resp_only", "", "(optional, default='False').", default=False, type=bool, action="store_true") cmdUtils.register_command("verbosity", "", "Logging level.", default=io.LogLevel.NoLogs.name, choices=[x.name for x in io.LogLevel]) args = cmdUtils.get_args() diff --git a/samples/command_line_utils.py b/samples/command_line_utils.py index 34fc47d8..53e18e8b 100644 --- a/samples/command_line_utils.py +++ b/samples/command_line_utils.py @@ -9,7 +9,7 @@ def __init__(self, description) -> None: self.parser = argparse.ArgumentParser(description="Send and receive messages through and MQTT connection.") self.commands = {} - def register_command(self, command_name, example_input, help_output, required=False, type=None, default=None, choices=None): + def register_command(self, command_name, example_input, help_output, required=False, type=None, default=None, choices=None, action=None): self.commands[command_name] = { "name":command_name, "example_input":example_input, @@ -17,7 +17,8 @@ def register_command(self, command_name, example_input, help_output, required=Fa "required": required, "type": type, "default": default, - "choices": choices + "choices": choices, + "action": action } def remove_command(self, command_name): @@ -27,12 +28,16 @@ def remove_command(self, command_name): def get_args(self): # add all the commands for command in self.commands.values(): - self.parser.add_argument("--" + command["name"], metavar=command["example_input"], help=command["help_output"], - required=command["required"], type=command["type"], default=command["default"], choices=command["choices"]) + if not command["action"] is None: + self.parser.add_argument("--" + command["name"], action=command["action"], help=command["help_output"], + required=command["required"], default=command["default"]) + else: + self.parser.add_argument("--" + command["name"], metavar=command["example_input"], help=command["help_output"], + required=command["required"], type=command["type"], default=command["default"], choices=command["choices"]) return self.parser.parse_args() - def update_command(self, command_name, new_example_input=None, new_help_output=None, new_required=None, new_type=None, new_default=None): + def update_command(self, command_name, new_example_input=None, new_help_output=None, new_required=None, new_type=None, new_default=None, new_action=None): if command_name in self.commands.keys(): if new_example_input: self.commands[command_name]["example_input"] = new_example_input @@ -44,6 +49,8 @@ def update_command(self, command_name, new_example_input=None, new_help_output=N self.commands[command_name]["type"] = new_type if new_default: self.commands[command_name]["default"] = new_default + if new_action: + self.commands[command_name]["action"] = new_action def add_common_mqtt_commands(self): self.register_command("endpoint", "", "The endpoint of the mqtt server not including a port.", True, str) diff --git a/samples/fleetprovisioning.py b/samples/fleetprovisioning.py index a5e0fcfa..362afa06 100644 --- a/samples/fleetprovisioning.py +++ b/samples/fleetprovisioning.py @@ -31,7 +31,7 @@ cmdUtils = command_line_utils.CommandLineUtils("Fleet Provisioning - Provision device using either the keys or CSR.") cmdUtils.add_common_mqtt_commands() cmdUtils.register_command("client_id", "", "Client ID to use for MQTT connection (optional, default='test-*').", default="test-" + str(uuid4())) -cmdUtils.register_command("use_websocket", "", "If specified, uses a websocket over https (optional).", default=False) +cmdUtils.register_command("use_websocket", "", "If specified, uses a websocket over https (optional).", default=False, action="store_true") cmdUtils.register_command("signing_region", "", "Used for websocket signer. It should only be specified if websockets are used (optional, default='us-east-1')", default="us-east-1") cmdUtils.register_command("proxy_host", "", "Host name of the http proxy to use (optional)") @@ -40,7 +40,7 @@ cmdUtils.register_command("csr", "", "Path to CSR in Pem format (optional).") cmdUtils.register_command("template_name", "", "The name of your provisioning template.") cmdUtils.register_command("template_parameters", "", "Template parameters json.") -cmdUtils.update_command("cert", new_help_output="Path to your certificate in PEM format. If this is not set you must specify use_websocket", new_required=False) +cmdUtils.update_command("cert", new_help_output="Path to your certificate in PEM format. If this is not set you must specify use_websocket") args = cmdUtils.get_args() # Using globals to simplify sample code diff --git a/samples/jobs.py b/samples/jobs.py index aad6afc7..2c40faa5 100644 --- a/samples/jobs.py +++ b/samples/jobs.py @@ -43,7 +43,7 @@ cmdUtils.register_command("client_id", "", "Client ID to use for MQTT connection (optional, default='test-*').", default="test-" + str(uuid4())) cmdUtils.register_command("thing_name", "", "The name assigned to your IoT Thing", required=True) cmdUtils.register_command("job_time", "", "Emulate working on a job by sleeping this many seconds (optional, default='5')", default=5) -cmdUtils.register_command("use_websocket", "", "If specified, uses a websocket over https (optional).", default=False) +cmdUtils.register_command("use_websocket", "", "If specified, uses a websocket over https (optional).", default=False, action="store_true") cmdUtils.register_command("signing_region", "", "Used for websocket signer. It should only be specified if websockets are used (optional, default='us-east-1')", default="us-east-1") cmdUtils.register_command("proxy_host", "", "Host name of the http proxy to use (optional)") diff --git a/samples/pubsub.py b/samples/pubsub.py index dd313f73..d044d295 100644 --- a/samples/pubsub.py +++ b/samples/pubsub.py @@ -26,13 +26,13 @@ cmdUtils.register_command("topic", "", "Topic to publish, subscribe to (optional, default='test/topic').", default="test/topic") cmdUtils.register_command("message", "", "The message to send in the payload (optional, default='Hello World!').", default="Hello World!") cmdUtils.register_command("count", "", "The number of messages to send (optional, default='10').", default=10, type=int) -cmdUtils.register_command("use_websocket", "", "If specified, uses a websocket over https (optional).", default=False) +cmdUtils.register_command("use_websocket", "", "If specified, uses a websocket over https (optional).", default=False, action="store_true") cmdUtils.register_command("signing_region", "", "Used for websocket signer. It should only be specified if websockets are used (optional, default='us-east-1')", default="us-east-1") cmdUtils.register_command("proxy_host", "", "Host name of the http proxy to use (optional)") cmdUtils.register_command("proxy_port", "", "Port of the http proxy to use (optional, default='8080')", type=int, default=8080) cmdUtils.register_command("verbosity", "", "Logging level.", default=io.LogLevel.NoLogs.name, choices=[x.name for x in io.LogLevel]) -cmdUtils.update_command("cert", new_help_output="Path to your certificate in PEM format. If this is not set you must specify use_websocket", new_required=False) +cmdUtils.update_command("cert", new_help_output="Path to your certificate in PEM format. If this is not set you must specify use_websocket") args = cmdUtils.get_args() io.init_logging(getattr(io.LogLevel, args.verbosity), 'stderr') diff --git a/samples/shadow.py b/samples/shadow.py index 53e738ab..3cac6aff 100644 --- a/samples/shadow.py +++ b/samples/shadow.py @@ -37,7 +37,7 @@ cmdUtils.register_command("client_id", "", "Client ID to use for MQTT connection (optional, default='test-*').", default="test-" + str(uuid4())) cmdUtils.register_command("thing_name", "", "The name assigned to your IoT Thing", required=True) cmdUtils.register_command("shadow_property", "", "The name of the shadow property you want to change (optional, default='color'", default="color") -cmdUtils.register_command("use_websocket", "", "If specified, uses a websocket over https (optional).", default=False) +cmdUtils.register_command("use_websocket", "", "If specified, uses a websocket over https (optional).", default=False, action="store_true") cmdUtils.register_command("signing_region", "", "Used for websocket signer. It should only be specified if websockets are used (optional, default='us-east-1')", default="us-east-1") cmdUtils.register_command("proxy_host", "", "Host name of the http proxy to use (optional)") From e3d2350cf566cc79f2f3bd06fc0ec2f232881c72 Mon Sep 17 00:00:00 2001 From: Noah Beard Date: Mon, 14 Mar 2022 18:39:07 -0400 Subject: [PATCH 04/10] Added additional command groups --- samples/basic_discovery.py | 5 ++--- samples/command_line_utils.py | 22 +++++++++++++++++++--- samples/fleetprovisioning.py | 9 +++------ samples/jobs.py | 9 +++------ samples/pkcs11_pubsub.py | 5 ++--- samples/pubsub.py | 12 ++++-------- 6 files changed, 33 insertions(+), 29 deletions(-) diff --git a/samples/basic_discovery.py b/samples/basic_discovery.py index 7f91c7fe..05063d9a 100644 --- a/samples/basic_discovery.py +++ b/samples/basic_discovery.py @@ -18,15 +18,14 @@ import command_line_utils; cmdUtils = command_line_utils.CommandLineUtils("Basic Discovery - Greengrass discovery example.") cmdUtils.add_common_mqtt_commands() +cmdUtils.add_common_topic_message_commands() +cmdUtils.add_common_logging_commands() cmdUtils.remove_command("endpoint") -cmdUtils.register_command("topic", "", "Topic to publish, subscribe to (optional, default='test/topic').", default="test/topic") cmdUtils.register_command("thing_name", "", "The name assigned to your IoT Thing", required=True) cmdUtils.register_command("mode", "", "The operation mode (optional, default='both').\nModes:%s"%str(allowed_actions), default='both') -cmdUtils.register_command("message", "", "The message to send in the payload (optional, default='Hello World!').", default="Hello World!") cmdUtils.register_command("region", "", "The region to connect through (optional, default='us-east-1').", default="us-east-1") cmdUtils.register_command("max_pub_ops", "", "The maximum number of publish operations (optional, default='10').", default=10, type=int) cmdUtils.register_command("print_discover_resp_only", "", "(optional, default='False').", default=False, type=bool, action="store_true") -cmdUtils.register_command("verbosity", "", "Logging level.", default=io.LogLevel.NoLogs.name, choices=[x.name for x in io.LogLevel]) args = cmdUtils.get_args() io.init_logging(getattr(LogLevel, args.verbosity), 'stderr') diff --git a/samples/command_line_utils.py b/samples/command_line_utils.py index 53e18e8b..9d5da21f 100644 --- a/samples/command_line_utils.py +++ b/samples/command_line_utils.py @@ -20,7 +20,7 @@ def register_command(self, command_name, example_input, help_output, required=Fa "choices": choices, "action": action } - + def remove_command(self, command_name): if command_name in self.commands.keys(): self.commands.pop(command_name) @@ -36,7 +36,7 @@ def get_args(self): required=command["required"], type=command["type"], default=command["default"], choices=command["choices"]) return self.parser.parse_args() - + def update_command(self, command_name, new_example_input=None, new_help_output=None, new_required=None, new_type=None, new_default=None, new_action=None): if command_name in self.commands.keys(): if new_example_input: @@ -51,9 +51,25 @@ def update_command(self, command_name, new_example_input=None, new_help_output=N self.commands[command_name]["default"] = new_default if new_action: self.commands[command_name]["action"] = new_action - + def add_common_mqtt_commands(self): self.register_command("endpoint", "", "The endpoint of the mqtt server not including a port.", True, str) self.register_command("key", "", "Path to your key in PEM format.", False, str) self.register_command("cert", "", "Path to your client certificate in PEM format.", False, str) self.register_command("ca_file", "", "Path to AmazonRootCA1.pem (optional, system trust store used by default)", False, str) + + def add_common_proxy_commands(self): + self.register_command("proxy_host", "", "Host name of the proxy server to connect through (optional)", False, str) + self.register_command("proxy_port", "", "Port of the http proxy to use (optional, default='8080')", type=int, default=8080) + + def add_common_topic_message_commands(self): + self.register_command("topic", "", "Topic to publish, subscribe to (optional, default='test/topic').", default="test/topic") + self.register_command("message", "", "The message to send in the payload (optional, default='Hello World!').", default="Hello World!") + + def add_common_websocket_commands(self): + self.register_command("use_websocket", "", "If specified, uses a websocket over https (optional).", default=False, action="store_true") + self.register_command("signing_region", "", + "Used for websocket signer. It should only be specified if websockets are used (optional, default='us-east-1')", default="us-east-1") + + def add_common_logging_commands(self): + self.register_command("verbosity", "", "Logging level.", default=io.LogLevel.NoLogs.name, choices=[x.name for x in io.LogLevel]) diff --git a/samples/fleetprovisioning.py b/samples/fleetprovisioning.py index 362afa06..79b15605 100644 --- a/samples/fleetprovisioning.py +++ b/samples/fleetprovisioning.py @@ -30,13 +30,10 @@ import command_line_utils; cmdUtils = command_line_utils.CommandLineUtils("Fleet Provisioning - Provision device using either the keys or CSR.") cmdUtils.add_common_mqtt_commands() +cmdUtils.add_common_websocket_commands() +cmdUtils.add_common_proxy_commands() +cmdUtils.add_common_logging_commands() cmdUtils.register_command("client_id", "", "Client ID to use for MQTT connection (optional, default='test-*').", default="test-" + str(uuid4())) -cmdUtils.register_command("use_websocket", "", "If specified, uses a websocket over https (optional).", default=False, action="store_true") -cmdUtils.register_command("signing_region", "", - "Used for websocket signer. It should only be specified if websockets are used (optional, default='us-east-1')", default="us-east-1") -cmdUtils.register_command("proxy_host", "", "Host name of the http proxy to use (optional)") -cmdUtils.register_command("proxy_port", "", "Port of the http proxy to use (optional, default='8080')", type=int, default=8080) -cmdUtils.register_command("verbosity", "", "Logging level.", default=io.LogLevel.NoLogs.name, choices=[x.name for x in io.LogLevel]) cmdUtils.register_command("csr", "", "Path to CSR in Pem format (optional).") cmdUtils.register_command("template_name", "", "The name of your provisioning template.") cmdUtils.register_command("template_parameters", "", "Template parameters json.") diff --git a/samples/jobs.py b/samples/jobs.py index 2c40faa5..20b382af 100644 --- a/samples/jobs.py +++ b/samples/jobs.py @@ -40,15 +40,12 @@ import command_line_utils; cmdUtils = command_line_utils.CommandLineUtils("Jobs - Recieve and execute operations on the device.") cmdUtils.add_common_mqtt_commands() +cmdUtils.add_common_websocket_commands() +cmdUtils.add_common_proxy_commands() +cmdUtils.add_common_logging_commands() cmdUtils.register_command("client_id", "", "Client ID to use for MQTT connection (optional, default='test-*').", default="test-" + str(uuid4())) cmdUtils.register_command("thing_name", "", "The name assigned to your IoT Thing", required=True) cmdUtils.register_command("job_time", "", "Emulate working on a job by sleeping this many seconds (optional, default='5')", default=5) -cmdUtils.register_command("use_websocket", "", "If specified, uses a websocket over https (optional).", default=False, action="store_true") -cmdUtils.register_command("signing_region", "", - "Used for websocket signer. It should only be specified if websockets are used (optional, default='us-east-1')", default="us-east-1") -cmdUtils.register_command("proxy_host", "", "Host name of the http proxy to use (optional)") -cmdUtils.register_command("proxy_port", "", "Port of the http proxy to use (optional, default='8080')", type=int, default=8080) -cmdUtils.register_command("verbosity", "", "Logging level.", default=io.LogLevel.NoLogs.name, choices=[x.name for x in io.LogLevel]) args = cmdUtils.get_args() mqtt_connection = None diff --git a/samples/pkcs11_pubsub.py b/samples/pkcs11_pubsub.py index 1d37f1ba..3e3875af 100644 --- a/samples/pkcs11_pubsub.py +++ b/samples/pkcs11_pubsub.py @@ -23,6 +23,8 @@ import command_line_utils; cmdUtils = command_line_utils.CommandLineUtils("PKCS#11 PubSub - Send and recieve messages through an MQTT connection.") cmdUtils.add_common_mqtt_commands() +cmdUtils.add_common_topic_message_commands() +cmdUtils.add_common_logging_commands() cmdUtils.register_command("port", "", "Connection port. AWS IoT supports 433 and 8883 (optional, default=auto).", type=int) cmdUtils.register_command("pkcs11_lib", "", "Path to PKCS#11 Library", required=True) cmdUtils.register_command("pin", "", "User PIN for logging into PKCS#11 token.", required=True) @@ -30,10 +32,7 @@ cmdUtils.register_command("slot_id", "", "Slot ID containing the PKCS#11 token to use (optional).") cmdUtils.register_command("key_label", "", "Label of private key on the PKCS#11 token (optional).") cmdUtils.register_command("client_id", "", "Client ID to use for MQTT connection (optional, default='test-*').", default="test-" + str(uuid4())) -cmdUtils.register_command("topic", "", "Topic to publish, subscribe to (optional, default='test/topic').", default="test/topic") -cmdUtils.register_command("message", "", "The message to send in the payload (optional, default='Hello World!').", default="Hello World!") cmdUtils.register_command("count", "", "The number of messages to send (optional, default='10').", default=10, type=int) -cmdUtils.register_command("verbosity", "", "Logging level.", default=io.LogLevel.NoLogs.name, choices=[x.name for x in io.LogLevel]) args = cmdUtils.get_args() io.init_logging(getattr(io.LogLevel, args.verbosity), 'stderr') diff --git a/samples/pubsub.py b/samples/pubsub.py index d044d295..69e5bb6c 100644 --- a/samples/pubsub.py +++ b/samples/pubsub.py @@ -21,17 +21,13 @@ import command_line_utils; cmdUtils = command_line_utils.CommandLineUtils("PubSub - Send and recieve messages through an MQTT connection.") cmdUtils.add_common_mqtt_commands() +cmdUtils.add_common_topic_message_commands() +cmdUtils.add_common_websocket_commands() +cmdUtils.add_common_proxy_commands() +cmdUtils.add_common_logging_commands() cmdUtils.register_command("port", "", "Connection port. AWS IoT supports 433 and 8883 (optional, default=auto).", type=int) cmdUtils.register_command("client_id", "", "Client ID to use for MQTT connection (optional, default='test-*').", default="test-" + str(uuid4())) -cmdUtils.register_command("topic", "", "Topic to publish, subscribe to (optional, default='test/topic').", default="test/topic") -cmdUtils.register_command("message", "", "The message to send in the payload (optional, default='Hello World!').", default="Hello World!") cmdUtils.register_command("count", "", "The number of messages to send (optional, default='10').", default=10, type=int) -cmdUtils.register_command("use_websocket", "", "If specified, uses a websocket over https (optional).", default=False, action="store_true") -cmdUtils.register_command("signing_region", "", - "Used for websocket signer. It should only be specified if websockets are used (optional, default='us-east-1')", default="us-east-1") -cmdUtils.register_command("proxy_host", "", "Host name of the http proxy to use (optional)") -cmdUtils.register_command("proxy_port", "", "Port of the http proxy to use (optional, default='8080')", type=int, default=8080) -cmdUtils.register_command("verbosity", "", "Logging level.", default=io.LogLevel.NoLogs.name, choices=[x.name for x in io.LogLevel]) cmdUtils.update_command("cert", new_help_output="Path to your certificate in PEM format. If this is not set you must specify use_websocket") args = cmdUtils.get_args() From c2a78bb305f21bb0b42bba59998a493899cb1c55 Mon Sep 17 00:00:00 2001 From: Noah Beard Date: Wed, 23 Mar 2022 12:10:56 -0400 Subject: [PATCH 05/10] Split PubSub sample into smaller connect samples --- samples/README.md | 102 +++++++++++++++++++++++-- samples/basic_connect.py | 71 +++++++++++++++++ samples/basic_discovery.py | 48 ++++++------ samples/command_line_utils.py | 119 +++++++++++++++++++++++++---- samples/fleetprovisioning.py | 65 +++++----------- samples/jobs.py | 57 ++++---------- samples/pkcs11_connect.py | 85 +++++++++++++++++++++ samples/pkcs11_pubsub.py | 139 ---------------------------------- samples/pubsub.py | 75 ++++++------------ samples/shadow.py | 77 +++++-------------- samples/websocket_connect.py | 68 +++++++++++++++++ 11 files changed, 520 insertions(+), 386 deletions(-) create mode 100644 samples/basic_connect.py create mode 100644 samples/pkcs11_connect.py delete mode 100644 samples/pkcs11_pubsub.py create mode 100644 samples/websocket_connect.py diff --git a/samples/README.md b/samples/README.md index c6aea304..f039f279 100644 --- a/samples/README.md +++ b/samples/README.md @@ -7,6 +7,16 @@ * [Fleet Provisioning](#fleet-provisioning) * [Greengrass Discovery](#greengrass-discovery) +## Build instructions + +First, install the aws-iot-devices-sdk-python-v2 with following the instructions from [Installation](../README.md#Installation). + +Then change into the samples directory to run the Python commands to execute the samples. You can view the commands of a sample like this: + +``` sh +python3 pubsub.py --help +``` + ## PubSub This sample uses the @@ -20,11 +30,6 @@ Status updates are continually printed to the console. Source: `samples/pubsub.py` -Run the sample like this: -``` sh -python3 pubsub.py --endpoint --ca_file --cert --key -``` - Your Thing's [Policy](https://docs.aws.amazon.com/iot/latest/developerguide/iot-policies.html) must provide privileges for this sample to connect, subscribe, publish, @@ -69,14 +74,95 @@ and receive. +Run the sample like this: +``` sh +python3 pubsub.py --endpoint --ca_file --cert --key +``` + +## Basic Connect + +This sample makes an MQTT connection using a certificate and key file. On startup, the device connects to the server using the certificate and key files, and then disconnects. +This sample is for reference on connecting via certificate and key files. + +Source: `samples/basic_connect.py` + +Your Thing's +[Policy](https://docs.aws.amazon.com/iot/latest/developerguide/iot-policies.html) +must provide privileges for this sample to connect, subscribe, publish, +and receive. + +
+(see sample policy) +
+{
+  "Version": "2012-10-17",
+  "Statement": [
+    {
+      "Effect": "Allow",
+      "Action": [
+        "iot:Connect"
+      ],
+      "Resource": [
+        "arn:aws:iot:region:account:client/test-*"
+      ]
+    }
+  ]
+}
+
+
+ +Run the sample like this: +``` sh +python3 basic_connect.py --endpoint --ca_file --cert --key +``` + +## Websocket Connect + +This sample makes an MQTT connection via websockets and then disconnects. On startup, the device connects to the server via websockets and then disconnects. +This sample is for reference on connecting via websockets. + +Source: `samples/websocket_connect.py` + +Your Thing's +[Policy](https://docs.aws.amazon.com/iot/latest/developerguide/iot-policies.html) +must provide privileges for this sample to connect, subscribe, publish, +and receive. + +
+(see sample policy) +
+{
+  "Version": "2012-10-17",
+  "Statement": [
+    {
+      "Effect": "Allow",
+      "Action": [
+        "iot:Connect"
+      ],
+      "Resource": [
+        "arn:aws:iot:region:account:client/test-*"
+      ]
+    }
+  ]
+}
+
+
+ +Run the sample like this: +``` sh +python3 websocket_connect.py --endpoint --ca_file --signing_region +``` + +Note that using Websockets will attempt to fetch the AWS credentials from your enviornment variables or local files. See the [authorizing direct AWS](https://docs.aws.amazon.com/iot/latest/developerguide/authorizing-direct-aws.html) page for documentation on how to get the AWS credentials, which then you can set to the `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS`, and `AWS_SESSION_TOKEN` environment variables. + ## PKCS#11 PubSub -This sample is similar to the [Pub-Sub](#pubsub), +This sample is similar to the [Basic Connect](#basic-connect), but the private key for mutual TLS is stored on a PKCS#11 compatible smart card or Hardware Security Module (HSM) WARNING: Unix only. Currently, TLS integration with PKCS#11 is only available on Unix devices. -source: `samples/pkcs11_pubsub.py` +source: `samples/pkcs11_connect.py` To run this sample using [SoftHSM2](https://www.opendnssec.org/softhsm/) as the PKCS#11 device: @@ -119,7 +205,7 @@ To run this sample using [SoftHSM2](https://www.opendnssec.org/softhsm/) as the 5) Now you can run the sample: ```sh - python3 pkcs11_pubsub.py --endpoint --ca_file --cert --pkcs11_lib --pin --token_label --key_label + python3 pkcs11_connect.py --endpoint --ca_file --cert --pkcs11_lib --pin --token_label --key_label ## Shadow diff --git a/samples/basic_connect.py b/samples/basic_connect.py new file mode 100644 index 00000000..f11423fa --- /dev/null +++ b/samples/basic_connect.py @@ -0,0 +1,71 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0. + +from awscrt import http +from awsiot import mqtt_connection_builder +from uuid import uuid4 + +# This sample shows how to create a MQTT connection using a certificate file and key file. +# This sample is intended to be used as a reference for making MQTT connections. + +# Parse arguments +import command_line_utils +cmdUtils = command_line_utils.CommandLineUtils("PubSub - Send and recieve messages through an MQTT connection.") +cmdUtils.add_common_mqtt_commands() +cmdUtils.add_common_proxy_commands() +cmdUtils.add_common_logging_commands() +cmdUtils.register_command("key", "", "Path to your key in PEM format.", True, str) +cmdUtils.register_command("cert", "", "Path to your client certificate in PEM format.", True, str) +cmdUtils.register_command("port", "", + "Connection port for direct connection. " + + "AWS IoT supports 433 and 8883 (optional, default=8883).", + False, int) +cmdUtils.register_command("client_id", "", + "Client ID to use for MQTT connection (optional, default='test-*').", + default="test-" + str(uuid4())) +# Needs to be called so the command utils parse the commands +cmdUtils.get_args() + +# Callback when connection is accidentally lost. +def on_connection_interrupted(connection, error, **kwargs): + print("Connection interrupted. error: {}".format(error)) + +# Callback when an interrupted connection is re-established. +def on_connection_resumed(connection, return_code, session_present, **kwargs): + print("Connection resumed. return_code: {} session_present: {}".format(return_code, session_present)) + + +if __name__ == '__main__': + proxy_options = None + if cmdUtils.get_command(cmdUtils.m_cmd_proxy_host) and cmdUtils.get_command(cmdUtils.m_cmd_proxy_port): + proxy_options = http.HttpProxyOptions( + host_name=cmdUtils.get_command(cmdUtils.m_cmd_proxy_host), + port=cmdUtils.get_command(cmdUtils.m_cmd_proxy_port)) + + mqtt_connection = mqtt_connection_builder.mtls_from_path( + endpoint=cmdUtils.get_command_required(cmdUtils.m_cmd_endpoint), + port=cmdUtils.get_command_required("port"), + cert_filepath=cmdUtils.get_command_required("cert"), + pri_key_filepath=cmdUtils.get_command_required("key"), + ca_filepath=cmdUtils.get_command(cmdUtils.m_cmd_ca_file), + on_connection_interrupted=on_connection_interrupted, + on_connection_resumed=on_connection_resumed, + client_id=cmdUtils.get_command_required("client_id"), + clean_session=False, + keep_alive_secs=30, + http_proxy_options=proxy_options) + + print("Connecting to {} with client ID '{}'...".format( + cmdUtils.get_command(cmdUtils.m_cmd_endpoint), cmdUtils.get_command("client_id"))) + + connect_future = mqtt_connection.connect() + + # Future.result() waits until a result is available + connect_future.result() + print("Connected!") + + # Disconnect + print("Disconnecting...") + disconnect_future = mqtt_connection.disconnect() + disconnect_future.result() + print("Disconnected!") diff --git a/samples/basic_discovery.py b/samples/basic_discovery.py index ee499b73..a82dddf7 100644 --- a/samples/basic_discovery.py +++ b/samples/basic_discovery.py @@ -1,15 +1,12 @@ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0. -import argparse import time -import uuid import json from concurrent.futures import Future from awscrt import io -from awscrt.io import LogLevel -from awscrt.mqtt import Connection, Client, QoS -from awsiot.greengrass_discovery import DiscoveryClient, DiscoverResponse +from awscrt.mqtt import QoS +from awsiot.greengrass_discovery import DiscoveryClient from awsiot import mqtt_connection_builder allowed_actions = ['both', 'publish', 'subscribe'] @@ -20,30 +17,31 @@ cmdUtils.add_common_mqtt_commands() cmdUtils.add_common_topic_message_commands() cmdUtils.add_common_logging_commands() +cmdUtils.register_command("key", "", "Path to your key in PEM format.", True, str) +cmdUtils.register_command("cert", "", "Path to your client certificate in PEM format.", True, str) cmdUtils.remove_command("endpoint") cmdUtils.register_command("thing_name", "", "The name assigned to your IoT Thing", required=True) cmdUtils.register_command("mode", "", "The operation mode (optional, default='both').\nModes:%s"%str(allowed_actions), default='both') -cmdUtils.register_command("region", "", "The region to connect through (optional, default='us-east-1').", default="us-east-1") +cmdUtils.register_command("region", "", "The region to connect through.", required=True) cmdUtils.register_command("max_pub_ops", "", "The maximum number of publish operations (optional, default='10').", default=10, type=int) cmdUtils.register_command("print_discover_resp_only", "", "(optional, default='False').", default=False, type=bool, action="store_true") -args = cmdUtils.get_args() +# Needs to be called so the command utils parse the commands +cmdUtils.get_args() -io.init_logging(getattr(LogLevel, args.verbosity), 'stderr') - -tls_options = io.TlsContextOptions.create_client_with_mtls_from_path(args.cert, args.key) -if args.ca_file: - tls_options.override_default_trust_store_from_path(None, args.ca_file) +tls_options = io.TlsContextOptions.create_client_with_mtls_from_path(cmdUtils.get_command_required("cert"), cmdUtils.get_command_required("key")) +if cmdUtils.get_command(cmdUtils.m_cmd_ca_file): + tls_options.override_default_trust_store_from_path(None, cmdUtils.get_command(cmdUtils.m_cmd_ca_file)) tls_context = io.ClientTlsContext(tls_options) socket_options = io.SocketOptions() print('Performing greengrass discovery...') -discovery_client = DiscoveryClient(io.ClientBootstrap.get_or_create_static_default(), socket_options, tls_context, args.region) -resp_future = discovery_client.discover(args.thing_name) +discovery_client = DiscoveryClient(io.ClientBootstrap.get_or_create_static_default(), socket_options, tls_context, cmdUtils.get_command_required("region")) +resp_future = discovery_client.discover(cmdUtils.get_command_required("thing_name")) discover_response = resp_future.result() print(discover_response) -if args.print_discover_resp_only: +if cmdUtils.get_command("print_discover_resp_only"): exit(0) @@ -65,12 +63,12 @@ def try_iot_endpoints(): mqtt_connection = mqtt_connection_builder.mtls_from_path( endpoint=connectivity_info.host_address, port=connectivity_info.port, - cert_filepath=args.cert, - pri_key_filepath=args.key, + cert_filepath=cmdUtils.get_command_required("cert"), + pri_key_filepath=cmdUtils.get_command_required("key"), ca_bytes=gg_group.certificate_authorities[0].encode('utf-8'), on_connection_interrupted=on_connection_interupted, on_connection_resumed=on_connection_resumed, - client_id=args.thing_name, + client_id=cmdUtils.get_command_required("thing_name"), clean_session=False, keep_alive_secs=30) @@ -87,25 +85,25 @@ def try_iot_endpoints(): mqtt_connection = try_iot_endpoints() -if args.mode == 'both' or args.mode == 'subscribe': +if cmdUtils.get_command("mode") == 'both' or cmdUtils.get_command("mode") == 'subscribe': def on_publish(topic, payload, dup, qos, retain, **kwargs): print('Publish received on topic {}'.format(topic)) print(payload) - subscribe_future, _ = mqtt_connection.subscribe(args.topic, QoS.AT_MOST_ONCE, on_publish) + subscribe_future, _ = mqtt_connection.subscribe(cmdUtils.get_command("topic"), QoS.AT_MOST_ONCE, on_publish) subscribe_result = subscribe_future.result() loop_count = 0 -while loop_count < args.max_pub_ops: - if args.mode == 'both' or args.mode == 'publish': +while loop_count < cmdUtils.get_command("max_pub_ops"): + if cmdUtils.get_command("mode") == 'both' or cmdUtils.get_command("mode") == 'publish': message = {} - message['message'] = args.message + message['message'] = cmdUtils.get_command("message") message['sequence'] = loop_count messageJson = json.dumps(message) - pub_future, _ = mqtt_connection.publish(args.topic, messageJson, QoS.AT_MOST_ONCE) + pub_future, _ = mqtt_connection.publish(cmdUtils.get_command("topic"), messageJson, QoS.AT_MOST_ONCE) pub_future.result() - print('Published topic {}: {}\n'.format(args.topic, messageJson)) + print('Published topic {}: {}\n'.format(cmdUtils.get_command("topic"), messageJson)) loop_count += 1 time.sleep(1) diff --git a/samples/command_line_utils.py b/samples/command_line_utils.py index 9d5da21f..6b7b33ba 100644 --- a/samples/command_line_utils.py +++ b/samples/command_line_utils.py @@ -2,12 +2,14 @@ # SPDX-License-Identifier: Apache-2.0. import argparse -from awscrt import io +from awscrt import io, http, auth +from awsiot import mqtt_connection_builder class CommandLineUtils: def __init__(self, description) -> None: self.parser = argparse.ArgumentParser(description="Send and receive messages through and MQTT connection.") self.commands = {} + self.parsed_commands = None def register_command(self, command_name, example_input, help_output, required=False, type=None, default=None, choices=None, action=None): self.commands[command_name] = { @@ -26,6 +28,10 @@ def remove_command(self, command_name): self.commands.pop(command_name) def get_args(self): + # if we have already parsed, then return the cached parsed commands + if self.parsed_commands is not None: + return self.parsed_commands + # add all the commands for command in self.commands.values(): if not command["action"] is None: @@ -35,7 +41,11 @@ def get_args(self): self.parser.add_argument("--" + command["name"], metavar=command["example_input"], help=command["help_output"], required=command["required"], type=command["type"], default=command["default"], choices=command["choices"]) - return self.parser.parse_args() + self.parsed_commands = self.parser.parse_args() + # Automatically start logging if it is set + if self.parsed_commands.verbosity: + io.init_logging(getattr(io.LogLevel, self.parsed_commands.verbosity), 'stderr') + return self.parsed_commands def update_command(self, command_name, new_example_input=None, new_help_output=None, new_required=None, new_type=None, new_default=None, new_action=None): if command_name in self.commands.keys(): @@ -53,23 +63,100 @@ def update_command(self, command_name, new_example_input=None, new_help_output=N self.commands[command_name]["action"] = new_action def add_common_mqtt_commands(self): - self.register_command("endpoint", "", "The endpoint of the mqtt server not including a port.", True, str) - self.register_command("key", "", "Path to your key in PEM format.", False, str) - self.register_command("cert", "", "Path to your client certificate in PEM format.", False, str) - self.register_command("ca_file", "", "Path to AmazonRootCA1.pem (optional, system trust store used by default)", False, str) + self.register_command(self.m_cmd_endpoint, "", "The endpoint of the mqtt server not including a port.", True, str) + self.register_command(self.m_cmd_ca_file, "", "Path to AmazonRootCA1.pem (optional, system trust store used by default)", False, str) def add_common_proxy_commands(self): - self.register_command("proxy_host", "", "Host name of the proxy server to connect through (optional)", False, str) - self.register_command("proxy_port", "", "Port of the http proxy to use (optional, default='8080')", type=int, default=8080) + self.register_command(self.m_cmd_proxy_host, "", "Host name of the proxy server to connect through (optional)", False, str) + self.register_command(self.m_cmd_proxy_port, "", "Port of the http proxy to use (optional, default='8080')", type=int, default=8080) def add_common_topic_message_commands(self): - self.register_command("topic", "", "Topic to publish, subscribe to (optional, default='test/topic').", default="test/topic") - self.register_command("message", "", "The message to send in the payload (optional, default='Hello World!').", default="Hello World!") - - def add_common_websocket_commands(self): - self.register_command("use_websocket", "", "If specified, uses a websocket over https (optional).", default=False, action="store_true") - self.register_command("signing_region", "", - "Used for websocket signer. It should only be specified if websockets are used (optional, default='us-east-1')", default="us-east-1") + self.register_command(self.m_cmd_topic, "", "Topic to publish, subscribe to (optional, default='test/topic').", default="test/topic") + self.register_command(self.m_cmd_message, "", "The message to send in the payload (optional, default='Hello World!').", default="Hello World!") def add_common_logging_commands(self): - self.register_command("verbosity", "", "Logging level.", default=io.LogLevel.NoLogs.name, choices=[x.name for x in io.LogLevel]) + self.register_command(self.m_cmd_verbosity, "", "Logging level.", default=io.LogLevel.NoLogs.name, choices=[x.name for x in io.LogLevel]) + + """ + Returns the command if it exists and has been passed to the console, otherwise it will print the help for the sample and exit the application. + """ + def get_command_required(self, command_name, message=None): + if hasattr(self.parsed_commands, command_name): + return getattr(self.parsed_commands, command_name) + else: + self.parser.print_help() + print("Command --" + command_name + " required.") + if message is not None: + print(message) + exit() + + """ + Returns the command if it exists and has been passed to the console, otherwise it returns whatever is passed as the default. + """ + def get_command(self, command_name, default=None): + if hasattr(self.parsed_commands, command_name): + return getattr(self.parsed_commands, command_name) + return default + + def build_websocket_mqtt_connection(self, on_connection_interrupted, on_connection_resumed): + proxy_options = self.get_proxy_options_for_mqtt_connection() + credentials_provider = auth.AwsCredentialsProvider.new_default_chain() + mqtt_connection = mqtt_connection_builder.websockets_with_default_aws_signing( + endpoint=self.get_command_required(self.m_cmd_endpoint), + region=self.get_command_required(self.m_cmd_signing_region), + credentials_provider=credentials_provider, + http_proxy_options=proxy_options, + ca_filepath=self.get_command(self.m_cmd_ca_file), + on_connection_interrupted=on_connection_interrupted, + on_connection_resumed=on_connection_resumed, + client_id=self.get_command_required("client_id"), + clean_session=False, + keep_alive_secs=30) + return mqtt_connection + + def build_direct_mqtt_connection(self, on_connection_interrupted, on_connection_resumed): + proxy_options = self.get_proxy_options_for_mqtt_connection() + mqtt_connection = mqtt_connection_builder.mtls_from_path( + endpoint=self.get_command_required(self.m_cmd_endpoint), + port=self.get_command_required("port"), + cert_filepath=self.get_command_required(self.m_cmd_cert_file), + pri_key_filepath=self.get_command_required(self.m_cmd_key_file), + ca_filepath=self.get_command(self.m_cmd_ca_file), + on_connection_interrupted=on_connection_interrupted, + on_connection_resumed=on_connection_resumed, + client_id=self.get_command_required("client_id"), + clean_session=False, + keep_alive_secs=30, + http_proxy_options=proxy_options) + return mqtt_connection + + def build_mqtt_connection(self, on_connection_interrupted, on_connection_resumed): + if self.get_command(self.m_cmd_signing_region) is not None: + return self.build_websocket_mqtt_connection(on_connection_interrupted, on_connection_resumed) + else: + return self.build_direct_mqtt_connection(on_connection_interrupted, on_connection_resumed) + + def get_proxy_options_for_mqtt_connection(self): + proxy_options = None + if self.parsed_commands.proxy_host and self.parsed_commands.proxy_port: + proxy_options = http.HttpProxyOptions(host_name=self.parsed_commands.proxy_host, port=self.parsed_commands.proxy_port) + return proxy_options + + + # Constants for commonly used/needed commands + m_cmd_endpoint = "endpoint" + m_cmd_ca_file = "ca_file" + m_cmd_cert_file = "cert" + m_cmd_key_file = "key" + m_cmd_proxy_host = "proxy_host" + m_cmd_proxy_port = "proxy_port" + m_cmd_signing_region = "signing_region" + m_cmd_pkcs11_lib = "pkcs11_lib" + m_cmd_pkcs11_cert = "cert" + m_cmd_pkcs11_pin = "pkcs11_pin" + m_cmd_pkcs11_token = "token_label" + m_cmd_pkcs11_slot = "slot_id" + m_cmd_pkcs11_key = "key_label" + m_cmd_message = "message" + m_cmd_topic = "topic" + m_cmd_verbosity = "verbosity" diff --git a/samples/fleetprovisioning.py b/samples/fleetprovisioning.py index 9f007383..4d786021 100644 --- a/samples/fleetprovisioning.py +++ b/samples/fleetprovisioning.py @@ -1,10 +1,8 @@ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0. -import argparse -from awscrt import auth, http, io, mqtt +from awscrt import mqtt from awsiot import iotidentity -from awsiot import mqtt_connection_builder from concurrent.futures import Future import sys import threading @@ -30,23 +28,22 @@ import command_line_utils; cmdUtils = command_line_utils.CommandLineUtils("Fleet Provisioning - Provision device using either the keys or CSR.") cmdUtils.add_common_mqtt_commands() -cmdUtils.add_common_websocket_commands() cmdUtils.add_common_proxy_commands() cmdUtils.add_common_logging_commands() +cmdUtils.register_command("key", "", "Path to your key in PEM format.", True, str) +cmdUtils.register_command("cert", "", "Path to your client certificate in PEM format.", True, str) cmdUtils.register_command("client_id", "", "Client ID to use for MQTT connection (optional, default='test-*').", default="test-" + str(uuid4())) +cmdUtils.register_command("port", "", "Connection port. AWS IoT supports 433 and 8883 (optional, default=auto).", type=int) cmdUtils.register_command("csr", "", "Path to CSR in Pem format (optional).") cmdUtils.register_command("template_name", "", "The name of your provisioning template.") cmdUtils.register_command("template_parameters", "", "Template parameters json.") -cmdUtils.update_command("cert", new_help_output="Path to your certificate in PEM format. If this is not set you must specify use_websocket") -args = cmdUtils.get_args() +# Needs to be called so the command utils parse the commands +cmdUtils.get_args() # Using globals to simplify sample code is_sample_done = threading.Event() - -io.init_logging(getattr(io.LogLevel, args.verbosity), 'stderr') mqtt_connection = None identity_client = None - createKeysAndCertificateResponse = None createCertificateFromCsrResponse = None registerThingResponse = None @@ -219,38 +216,10 @@ def waitForRegisterThingResponse(): if __name__ == '__main__': proxy_options = None - if (args.proxy_host): - proxy_options = http.HttpProxyOptions(host_name=args.proxy_host, port=args.proxy_port) - - if args.use_websocket == True: - credentials_provider = auth.AwsCredentialsProvider.new_default_chain() - mqtt_connection = mqtt_connection_builder.websockets_with_default_aws_signing( - endpoint=args.endpoint, - region=args.signing_region, - credentials_provider=credentials_provider, - http_proxy_options=proxy_options, - on_connection_interrupted=on_connection_interrupted, - on_connection_resumed=on_connection_resumed, - ca_filepath=args.ca_file, - client_id=args.client_id, - clean_session=False, - keep_alive_secs=30) - - else: - mqtt_connection = mqtt_connection_builder.mtls_from_path( - endpoint=args.endpoint, - cert_filepath=args.cert, - pri_key_filepath=args.key, - ca_filepath=args.ca_file, - client_id=args.client_id, - on_connection_interrupted=on_connection_interrupted, - on_connection_resumed=on_connection_resumed, - clean_session=False, - keep_alive_secs=30, - http_proxy_options=proxy_options) + mqtt_connection = cmdUtils.build_mqtt_connection(on_connection_interrupted, on_connection_resumed) print("Connecting to {} with client ID '{}'...".format( - args.endpoint, args.client_id)) + cmdUtils.get_command(cmdUtils.m_cmd_endpoint), cmdUtils.get_command("client_id"))) connected_future = mqtt_connection.connect() @@ -270,7 +239,7 @@ def waitForRegisterThingResponse(): # to succeed before publishing the corresponding "request". # Keys workflow if csr is not provided - if args.csr is None: + if cmdUtils.get_command("csr") is None: createkeysandcertificate_subscription_request = iotidentity.CreateKeysAndCertificateSubscriptionRequest() print("Subscribing to CreateKeysAndCertificate Accepted topic...") @@ -312,7 +281,7 @@ def waitForRegisterThingResponse(): createcertificatefromcsr_subscribed_rejected_future.result() - registerthing_subscription_request = iotidentity.RegisterThingSubscriptionRequest(template_name=args.template_name) + registerthing_subscription_request = iotidentity.RegisterThingSubscriptionRequest(template_name=cmdUtils.get_command_required("template_name")) print("Subscribing to RegisterThing Accepted topic...") registerthing_subscribed_accepted_future, _ = identity_client.subscribe_to_register_thing_accepted( @@ -331,7 +300,9 @@ def waitForRegisterThingResponse(): # Wait for subscription to succeed registerthing_subscribed_rejected_future.result() - if args.csr is None: + fleet_template_name = cmdUtils.get_command_required("template_name") + fleet_template_parameters = cmdUtils.get_command_required("template_parameters") + if cmdUtils.get_command("csr") is None: print("Publishing to CreateKeysAndCertificate...") publish_future = identity_client.publish_create_keys_and_certificate( request=iotidentity.CreateKeysAndCertificateRequest(), qos=mqtt.QoS.AT_LEAST_ONCE) @@ -343,12 +314,12 @@ def waitForRegisterThingResponse(): raise Exception('CreateKeysAndCertificate API did not succeed') registerThingRequest = iotidentity.RegisterThingRequest( - template_name=args.template_name, + template_name=fleet_template_name, certificate_ownership_token=createKeysAndCertificateResponse.certificate_ownership_token, - parameters=json.loads(args.template_parameters)) + parameters=json.loads(fleet_template_parameters)) else: print("Publishing to CreateCertificateFromCsr...") - csrPath = open(args.csr, 'r').read() + csrPath = open(cmdUtils.get_command("csr"), 'r').read() publish_future = identity_client.publish_create_certificate_from_csr( request=iotidentity.CreateCertificateFromCsrRequest(certificate_signing_request=csrPath), qos=mqtt.QoS.AT_LEAST_ONCE) @@ -360,9 +331,9 @@ def waitForRegisterThingResponse(): raise Exception('CreateCertificateFromCsr API did not succeed') registerThingRequest = iotidentity.RegisterThingRequest( - template_name=args.template_name, + template_name=fleet_template_name, certificate_ownership_token=createCertificateFromCsrResponse.certificate_ownership_token, - parameters=json.loads(args.template_parameters)) + parameters=json.loads(fleet_template_parameters)) print("Publishing to RegisterThing topic...") registerthing_publish_future = identity_client.publish_register_thing(registerThingRequest, mqtt.QoS.AT_LEAST_ONCE) diff --git a/samples/jobs.py b/samples/jobs.py index 6cac18cf..4a1ce437 100644 --- a/samples/jobs.py +++ b/samples/jobs.py @@ -40,17 +40,20 @@ import command_line_utils; cmdUtils = command_line_utils.CommandLineUtils("Jobs - Recieve and execute operations on the device.") cmdUtils.add_common_mqtt_commands() -cmdUtils.add_common_websocket_commands() cmdUtils.add_common_proxy_commands() cmdUtils.add_common_logging_commands() +cmdUtils.register_command("key", "", "Path to your key in PEM format.", True, str) +cmdUtils.register_command("cert", "", "Path to your client certificate in PEM format.", True, str) cmdUtils.register_command("client_id", "", "Client ID to use for MQTT connection (optional, default='test-*').", default="test-" + str(uuid4())) +cmdUtils.register_command("port", "", "Connection port. AWS IoT supports 433 and 8883 (optional, default=auto).", type=int) cmdUtils.register_command("thing_name", "", "The name assigned to your IoT Thing", required=True) -cmdUtils.register_command("job_time", "", "Emulate working on a job by sleeping this many seconds (optional, default='5')", default=5) -args = cmdUtils.get_args() +cmdUtils.register_command("job_time", "", "Emulate working on a job by sleeping this many seconds (optional, default='5')", default=5, type=int) +# Needs to be called so the command utils parse the commands +cmdUtils.get_args() mqtt_connection = None jobs_client = None -thing_name = "" +jobs_thing_name = cmdUtils.get_command_required("thing_name") class LockedData: def __init__(self): @@ -91,7 +94,7 @@ def try_start_next_job(): locked_data.is_next_job_waiting = False print("Publishing request to start next job...") - request = iotjobs.StartNextPendingJobExecutionRequest(thing_name=args.thing_name) + request = iotjobs.StartNextPendingJobExecutionRequest(thing_name=jobs_thing_name) publish_future = jobs_client.publish_start_next_pending_job_execution(request, mqtt.QoS.AT_LEAST_ONCE) publish_future.add_done_callback(on_publish_start_next_pending_job_execution) @@ -173,12 +176,12 @@ def on_start_next_pending_job_execution_rejected(rejected): def job_thread_fn(job_id, job_document): try: print("Starting local work on job...") - time.sleep(args.job_time) + time.sleep(cmdUtils.get_command("job_time")) print("Done working on job.") print("Publishing request to update job status to SUCCEEDED...") request = iotjobs.UpdateJobExecutionRequest( - thing_name=args.thing_name, + thing_name=jobs_thing_name, job_id=job_id, status=iotjobs.JobStatus.SUCCEEDED) publish_future = jobs_client.publish_update_job_execution(request, mqtt.QoS.AT_LEAST_ONCE) @@ -210,39 +213,9 @@ def on_update_job_execution_rejected(rejected): rejected.code, rejected.message)) if __name__ == '__main__': - # Process input args - thing_name = args.thing_name - io.init_logging(getattr(io.LogLevel, args.verbosity), 'stderr') - - proxy_options = None - if (args.proxy_host): - proxy_options = http.HttpProxyOptions(host_name=args.proxy_host, port=args.proxy_port) - - if args.use_websocket == True: - credentials_provider = auth.AwsCredentialsProvider.new_default_chain() - mqtt_connection = mqtt_connection_builder.websockets_with_default_aws_signing( - endpoint=args.endpoint, - region=args.signing_region, - credentials_provider=credentials_provider, - http_proxy_options=proxy_options, - ca_filepath=args.ca_file, - client_id=args.client_id, - clean_session=False, - keep_alive_secs=30) - - else: - mqtt_connection = mqtt_connection_builder.mtls_from_path( - endpoint=args.endpoint, - cert_filepath=args.cert, - pri_key_filepath=args.key, - ca_filepath=args.ca_file, - client_id=args.client_id, - clean_session=False, - keep_alive_secs=30, - http_proxy_options=proxy_options) - + mqtt_connection = cmdUtils.build_mqtt_connection(None, None) print("Connecting to {} with client ID '{}'...".format( - args.endpoint, args.client_id)) + cmdUtils.get_command(cmdUtils.m_cmd_endpoint), cmdUtils.get_command("client_id"))) connected_future = mqtt_connection.connect() @@ -262,7 +235,7 @@ def on_update_job_execution_rejected(rejected): # to succeed before publishing the corresponding "request". print("Subscribing to Next Changed events...") changed_subscription_request = iotjobs.NextJobExecutionChangedSubscriptionRequest( - thing_name=args.thing_name) + thing_name=jobs_thing_name) subscribed_future, _ = jobs_client.subscribe_to_next_job_execution_changed_events( request=changed_subscription_request, @@ -274,7 +247,7 @@ def on_update_job_execution_rejected(rejected): print("Subscribing to Start responses...") start_subscription_request = iotjobs.StartNextPendingJobExecutionSubscriptionRequest( - thing_name=args.thing_name) + thing_name=jobs_thing_name) subscribed_accepted_future, _ = jobs_client.subscribe_to_start_next_pending_job_execution_accepted( request=start_subscription_request, qos=mqtt.QoS.AT_LEAST_ONCE, @@ -293,7 +266,7 @@ def on_update_job_execution_rejected(rejected): # Note that we subscribe to "+", the MQTT wildcard, to receive # responses about any job-ID. update_subscription_request = iotjobs.UpdateJobExecutionSubscriptionRequest( - thing_name=args.thing_name, + thing_name=jobs_thing_name, job_id='+') subscribed_accepted_future, _ = jobs_client.subscribe_to_update_job_execution_accepted( diff --git a/samples/pkcs11_connect.py b/samples/pkcs11_connect.py new file mode 100644 index 00000000..7d3b9da1 --- /dev/null +++ b/samples/pkcs11_connect.py @@ -0,0 +1,85 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0. + +from awscrt import io +from awsiot import mqtt_connection_builder +from uuid import uuid4 + +# This sample is similar to `samples/basic_connect.py` but the private key +# for mutual TLS is stored on a PKCS#11 compatible smart card or +# Hardware Security Module (HSM). +# +# See `samples/README.md` for instructions on setting up your PKCS#11 device +# to run this sample. +# +# WARNING: Unix only. Currently, TLS integration with PKCS#11 is only available on Unix devices. + +# Parse arguments +import command_line_utils +cmdUtils = command_line_utils.CommandLineUtils("PubSub - Send and recieve messages through an MQTT connection.") +cmdUtils.add_common_mqtt_commands() +cmdUtils.add_common_proxy_commands() +cmdUtils.add_common_logging_commands() +cmdUtils.register_command("cert", "", "Path to your client certificate in PEM format.", True, str) +cmdUtils.register_command("client_id", "", + "Client ID to use for MQTT connection (optional, default='test-*').", + default="test-" + str(uuid4())) +cmdUtils.register_command("port", "", + "Connection port. AWS IoT supports 433 and 8883 (optional, default=auto).", + type=int) +cmdUtils.register_command("pkcs11_lib", "", "Path to PKCS#11 Library", required=True) +cmdUtils.register_command("pin", "", "User PIN for logging into PKCS#11 token.", required=True) +cmdUtils.register_command("token_label", "", "Label of the PKCS#11 token to use (optional).") +cmdUtils.register_command("slot_id", "", "Slot ID containing the PKCS#11 token to use (optional).", False, int) +cmdUtils.register_command("key_label", "", "Label of private key on the PKCS#11 token (optional).") +# Needs to be called so the command utils parse the commands +cmdUtils.get_args() + +# Callback when connection is accidentally lost. +def on_connection_interrupted(connection, error, **kwargs): + print("Connection interrupted. error: {}".format(error)) + +# Callback when an interrupted connection is re-established. +def on_connection_resumed(connection, return_code, session_present, **kwargs): + print("Connection resumed. return_code: {} session_present: {}".format(return_code, session_present)) + + +if __name__ == '__main__': + pkcs11_lib_file = cmdUtils.get_command_required("pkcs11_lib") + print(f"Loading PKCS#11 library '{pkcs11_lib_file}' ...") + pkcs11_lib = io.Pkcs11Lib( + file=pkcs11_lib_file, + behavior=io.Pkcs11Lib.InitializeFinalizeBehavior.STRICT) + print("Loaded!") + + # Create MQTT connection + mqtt_connection = mqtt_connection_builder.mtls_with_pkcs11( + pkcs11_lib=pkcs11_lib, + user_pin=cmdUtils.get_command_required("pin"), + slot_id=cmdUtils.get_command("slot_id"), + token_label=cmdUtils.get_command("token_label"), + private_key_label=cmdUtils.get_command("key_label"), + cert_filepath=cmdUtils.get_command_required("cert"), + endpoint=cmdUtils.get_command_required(cmdUtils.m_cmd_endpoint), + port=cmdUtils.get_command("port"), + ca_filepath=cmdUtils.get_command(cmdUtils.m_cmd_ca_file), + on_connection_interrupted=on_connection_interrupted, + on_connection_resumed=on_connection_resumed, + client_id=cmdUtils.get_command_required("client_id"), + clean_session=False, + keep_alive_secs=30) + + print("Connecting to {} with client ID '{}'...".format( + cmdUtils.get_command("endpoint"), cmdUtils.get_command("client_id"))) + + connect_future = mqtt_connection.connect() + + # Future.result() waits until a result is available + connect_future.result() + print("Connected!") + + # Disconnect + print("Disconnecting...") + disconnect_future = mqtt_connection.disconnect() + disconnect_future.result() + print("Disconnected!") diff --git a/samples/pkcs11_pubsub.py b/samples/pkcs11_pubsub.py deleted file mode 100644 index ff64eca0..00000000 --- a/samples/pkcs11_pubsub.py +++ /dev/null @@ -1,139 +0,0 @@ -# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -# SPDX-License-Identifier: Apache-2.0. - -import argparse -from awscrt import io, mqtt -from awsiot import mqtt_connection_builder -import sys -import threading -import time -from uuid import uuid4 -import json - -# This sample is similar to `samples/pubsub.py` but the private key -# for mutual TLS is stored on a PKCS#11 compatible smart card or -# Hardware Security Module (HSM). -# -# See `samples/README.md` for instructions on setting up your PKCS#11 device -# to run this sample. -# -# WARNING: Unix only. Currently, TLS integration with PKCS#11 is only available on Unix devices. - -# Parse arguments -import command_line_utils; -cmdUtils = command_line_utils.CommandLineUtils("PKCS#11 PubSub - Send and recieve messages through an MQTT connection.") -cmdUtils.add_common_mqtt_commands() -cmdUtils.add_common_topic_message_commands() -cmdUtils.add_common_logging_commands() -cmdUtils.register_command("port", "", "Connection port. AWS IoT supports 433 and 8883 (optional, default=auto).", type=int) -cmdUtils.register_command("pkcs11_lib", "", "Path to PKCS#11 Library", required=True) -cmdUtils.register_command("pin", "", "User PIN for logging into PKCS#11 token.", required=True) -cmdUtils.register_command("token_label", "", "Label of the PKCS#11 token to use (optional).") -cmdUtils.register_command("slot_id", "", "Slot ID containing the PKCS#11 token to use (optional).") -cmdUtils.register_command("key_label", "", "Label of private key on the PKCS#11 token (optional).") -cmdUtils.register_command("client_id", "", "Client ID to use for MQTT connection (optional, default='test-*').", default="test-" + str(uuid4())) -cmdUtils.register_command("count", "", "The number of messages to send (optional, default='10').", default=10, type=int) -args = cmdUtils.get_args() - -io.init_logging(getattr(io.LogLevel, args.verbosity), 'stderr') - -received_count = 0 -received_all_event = threading.Event() - - -def on_connection_interrupted(connection, error, **kwargs): - # Callback when connection is accidentally lost. - print("Connection interrupted. error: {}".format(error)) - - -def on_connection_resumed(connection, return_code, session_present, **kwargs): - # Callback when an interrupted connection is re-established. - print("Connection resumed. return_code: {} session_present: {}".format(return_code, session_present)) - - -# Callback when the subscribed topic receives a message -def on_message_received(topic, payload, dup, qos, retain, **kwargs): - print("Received message from topic '{}': {}".format(topic, payload)) - global received_count - received_count += 1 - if received_count == args.count: - received_all_event.set() - - -if __name__ == '__main__': - print(f"Loading PKCS#11 library '{args.pkcs11_lib}' ...") - pkcs11_lib = io.Pkcs11Lib( - file=args.pkcs11_lib, - behavior=io.Pkcs11Lib.InitializeFinalizeBehavior.STRICT) - print("Loaded!") - - # Create MQTT connection - mqtt_connection = mqtt_connection_builder.mtls_with_pkcs11( - pkcs11_lib=pkcs11_lib, - user_pin=args.pin, - slot_id=int(args.slot_id) if args.slot_id else None, - token_label=args.token_label, - private_key_label=args.key_label, - cert_filepath=args.cert, - endpoint=args.endpoint, - port=args.port, - ca_filepath=args.ca_file, - on_connection_interrupted=on_connection_interrupted, - on_connection_resumed=on_connection_resumed, - client_id=args.client_id, - clean_session=False, - keep_alive_secs=30) - - print("Connecting to {} with client ID '{}'...".format( - args.endpoint, args.client_id)) - - connect_future = mqtt_connection.connect() - - # Future.result() waits until a result is available - connect_future.result() - print("Connected!") - - # Subscribe - print("Subscribing to topic '{}'...".format(args.topic)) - subscribe_future, packet_id = mqtt_connection.subscribe( - topic=args.topic, - qos=mqtt.QoS.AT_LEAST_ONCE, - callback=on_message_received) - - subscribe_result = subscribe_future.result() - print("Subscribed with {}".format(str(subscribe_result['qos']))) - - # Publish message to server desired number of times. - # This step is skipped if message is blank. - # This step loops forever if count was set to 0. - if args.message: - if args.count == 0: - print("Sending messages until program killed") - else: - print("Sending {} message(s)".format(args.count)) - - publish_count = 1 - while (publish_count <= args.count) or (args.count == 0): - message = "{} [{}]".format(args.message, publish_count) - print("Publishing message to topic '{}': {}".format(args.topic, message)) - message_json = json.dumps(message) - mqtt_connection.publish( - topic=args.topic, - payload=message_json, - qos=mqtt.QoS.AT_LEAST_ONCE) - time.sleep(1) - publish_count += 1 - - # Wait for all messages to be received. - # This waits forever if count was set to 0. - if args.count != 0 and not received_all_event.is_set(): - print("Waiting for all messages to be received...") - - received_all_event.wait() - print("{} message(s) received.".format(received_count)) - - # Disconnect - print("Disconnecting...") - disconnect_future = mqtt_connection.disconnect() - disconnect_future.result() - print("Disconnected!") diff --git a/samples/pubsub.py b/samples/pubsub.py index c348eb46..a8233d4f 100644 --- a/samples/pubsub.py +++ b/samples/pubsub.py @@ -1,10 +1,7 @@ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0. -import argparse -from distutils import command -from awscrt import io, mqtt, auth, http -from awsiot import mqtt_connection_builder +from awscrt import mqtt import sys import threading import time @@ -22,16 +19,15 @@ cmdUtils = command_line_utils.CommandLineUtils("PubSub - Send and recieve messages through an MQTT connection.") cmdUtils.add_common_mqtt_commands() cmdUtils.add_common_topic_message_commands() -cmdUtils.add_common_websocket_commands() cmdUtils.add_common_proxy_commands() cmdUtils.add_common_logging_commands() +cmdUtils.register_command("key", "", "Path to your key in PEM format.", True, str) +cmdUtils.register_command("cert", "", "Path to your client certificate in PEM format.", True, str) cmdUtils.register_command("port", "", "Connection port. AWS IoT supports 433 and 8883 (optional, default=auto).", type=int) cmdUtils.register_command("client_id", "", "Client ID to use for MQTT connection (optional, default='test-*').", default="test-" + str(uuid4())) cmdUtils.register_command("count", "", "The number of messages to send (optional, default='10').", default=10, type=int) -cmdUtils.update_command("cert", new_help_output="Path to your certificate in PEM format. If this is not set you must specify use_websocket") -args = cmdUtils.get_args() - -io.init_logging(getattr(io.LogLevel, args.verbosity), 'stderr') +# Needs to be called so the command utils parse the commands +cmdUtils.get_args() received_count = 0 received_all_event = threading.Event() @@ -68,55 +64,28 @@ def on_message_received(topic, payload, dup, qos, retain, **kwargs): print("Received message from topic '{}': {}".format(topic, payload)) global received_count received_count += 1 - if received_count == args.count: + if received_count == cmdUtils.get_command("count"): received_all_event.set() if __name__ == '__main__': - proxy_options = None - if (args.proxy_host): - proxy_options = http.HttpProxyOptions(host_name=args.proxy_host, port=args.proxy_port) - - if args.use_websocket == True: - credentials_provider = auth.AwsCredentialsProvider.new_default_chain() - mqtt_connection = mqtt_connection_builder.websockets_with_default_aws_signing( - endpoint=args.endpoint, - region=args.signing_region, - credentials_provider=credentials_provider, - http_proxy_options=proxy_options, - ca_filepath=args.ca_file, - on_connection_interrupted=on_connection_interrupted, - on_connection_resumed=on_connection_resumed, - client_id=args.client_id, - clean_session=False, - keep_alive_secs=30) - - else: - mqtt_connection = mqtt_connection_builder.mtls_from_path( - endpoint=args.endpoint, - port=args.port, - cert_filepath=args.cert, - pri_key_filepath=args.key, - ca_filepath=args.ca_file, - on_connection_interrupted=on_connection_interrupted, - on_connection_resumed=on_connection_resumed, - client_id=args.client_id, - clean_session=False, - keep_alive_secs=30, - http_proxy_options=proxy_options) + mqtt_connection = cmdUtils.build_mqtt_connection(on_connection_interrupted, on_connection_resumed) print("Connecting to {} with client ID '{}'...".format( - args.endpoint, args.client_id)) - + cmdUtils.get_command(cmdUtils.m_cmd_endpoint), cmdUtils.get_command("client_id"))) connect_future = mqtt_connection.connect() # Future.result() waits until a result is available connect_future.result() print("Connected!") + message_count = cmdUtils.get_command("count") + message_topic = cmdUtils.get_command(cmdUtils.m_cmd_topic) + message_string = cmdUtils.get_command(cmdUtils.m_cmd_message) + # Subscribe - print("Subscribing to topic '{}'...".format(args.topic)) + print("Subscribing to topic '{}'...".format(message_topic)) subscribe_future, packet_id = mqtt_connection.subscribe( - topic=args.topic, + topic=message_topic, qos=mqtt.QoS.AT_LEAST_ONCE, callback=on_message_received) @@ -126,19 +95,19 @@ def on_message_received(topic, payload, dup, qos, retain, **kwargs): # Publish message to server desired number of times. # This step is skipped if message is blank. # This step loops forever if count was set to 0. - if args.message: - if args.count == 0: + if message_string: + if message_count == 0: print ("Sending messages until program killed") else: - print ("Sending {} message(s)".format(args.count)) + print ("Sending {} message(s)".format(message_count)) publish_count = 1 - while (publish_count <= args.count) or (args.count == 0): - message = "{} [{}]".format(args.message, publish_count) - print("Publishing message to topic '{}': {}".format(args.topic, message)) + while (publish_count <= message_count) or (message_count == 0): + message = "{} [{}]".format(message_string, publish_count) + print("Publishing message to topic '{}': {}".format(message_topic, message)) message_json = json.dumps(message) mqtt_connection.publish( - topic=args.topic, + topic=message_topic, payload=message_json, qos=mqtt.QoS.AT_LEAST_ONCE) time.sleep(1) @@ -146,7 +115,7 @@ def on_message_received(topic, payload, dup, qos, retain, **kwargs): # Wait for all messages to be received. # This waits forever if count was set to 0. - if args.count != 0 and not received_all_event.is_set(): + if message_count != 0 and not received_all_event.is_set(): print("Waiting for all messages to be received...") received_all_event.wait() diff --git a/samples/shadow.py b/samples/shadow.py index 930ac064..9e0f9bf8 100644 --- a/samples/shadow.py +++ b/samples/shadow.py @@ -1,10 +1,8 @@ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0. -import argparse -from awscrt import auth, io, mqtt, http +from awscrt import mqtt from awsiot import iotshadow -from awsiot import mqtt_connection_builder from concurrent.futures import Future import sys import threading @@ -34,24 +32,22 @@ import command_line_utils; cmdUtils = command_line_utils.CommandLineUtils("Shadow - Keep a property in sync between device and server.") cmdUtils.add_common_mqtt_commands() +cmdUtils.add_common_proxy_commands() +cmdUtils.add_common_logging_commands() +cmdUtils.register_command("key", "", "Path to your key in PEM format.", True, str) +cmdUtils.register_command("cert", "", "Path to your client certificate in PEM format.", True, str) +cmdUtils.register_command("port", "", "Connection port. AWS IoT supports 433 and 8883 (optional, default=auto).", type=int) cmdUtils.register_command("client_id", "", "Client ID to use for MQTT connection (optional, default='test-*').", default="test-" + str(uuid4())) cmdUtils.register_command("thing_name", "", "The name assigned to your IoT Thing", required=True) cmdUtils.register_command("shadow_property", "", "The name of the shadow property you want to change (optional, default='color'", default="color") -cmdUtils.register_command("use_websocket", "", "If specified, uses a websocket over https (optional).", default=False, action="store_true") -cmdUtils.register_command("signing_region", "", - "Used for websocket signer. It should only be specified if websockets are used (optional, default='us-east-1')", default="us-east-1") -cmdUtils.register_command("proxy_host", "", "Host name of the http proxy to use (optional)") -cmdUtils.register_command("proxy_port", "", "Port of the http proxy to use (optional, default='8080')", type=int, default=8080) -cmdUtils.register_command("verbosity", "", "Logging level.", default=io.LogLevel.NoLogs.name, choices=[x.name for x in io.LogLevel]) -args = cmdUtils.get_args() +# Needs to be called so the command utils parse the commands +cmdUtils.get_args() # Using globals to simplify sample code is_sample_done = threading.Event() - mqtt_connection = None -shadow_client = None -thing_name = "" -shadow_property = "" +shadow_thing_name = cmdUtils.get_command_required("thing_name") +shadow_property = cmdUtils.get_command("shadow_property") SHADOW_VALUE_DEFAULT = "off" @@ -239,12 +235,11 @@ def change_shadow_value(value): token = str(uuid4()) # if the value is "clear shadow" then send a UpdateShadowRequest with None - # for both reported and desired to clear the shadow document completely - # of both. + # for both reported and desired to clear the shadow document completely. if value == "clear_shadow": tmp_state = iotshadow.ShadowState(reported=None, desired=None, reported_is_nullable=True, desired_is_nullable=True) request = iotshadow.UpdateShadowRequest( - thing_name=thing_name, + thing_name=shadow_thing_name, state=tmp_state, client_token=token, ) @@ -256,7 +251,7 @@ def change_shadow_value(value): value = None request = iotshadow.UpdateShadowRequest( - thing_name=thing_name, + thing_name=shadow_thing_name, state=iotshadow.ShadowState( reported={ shadow_property: value }, desired={ shadow_property: value }, @@ -290,40 +285,10 @@ def user_input_thread_fn(): break if __name__ == '__main__': - # Process input args - thing_name = args.thing_name - shadow_property = args.shadow_property - io.init_logging(getattr(io.LogLevel, args.verbosity), 'stderr') - - proxy_options = None - if (args.proxy_host): - proxy_options = http.HttpProxyOptions(host_name=args.proxy_host, port=args.proxy_port) - - if args.use_websocket == True: - credentials_provider = auth.AwsCredentialsProvider.new_default_chain() - mqtt_connection = mqtt_connection_builder.websockets_with_default_aws_signing( - endpoint=args.endpoint, - region=args.signing_region, - credentials_provider=credentials_provider, - http_proxy_options=proxy_options, - ca_filepath=args.ca_file, - client_id=args.client_id, - clean_session=True, - keep_alive_secs=30) - - else: - mqtt_connection = mqtt_connection_builder.mtls_from_path( - endpoint=args.endpoint, - cert_filepath=args.cert, - pri_key_filepath=args.key, - ca_filepath=args.ca_file, - client_id=args.client_id, - clean_session=True, - keep_alive_secs=30, - http_proxy_options=proxy_options) + mqtt_connection = cmdUtils.build_mqtt_connection(None, None) print("Connecting to {} with client ID '{}'...".format( - args.endpoint, args.client_id)) + cmdUtils.get_command(cmdUtils.m_cmd_endpoint), cmdUtils.get_command("client_id"))) connected_future = mqtt_connection.connect() @@ -343,12 +308,12 @@ def user_input_thread_fn(): # to succeed before publishing the corresponding "request". print("Subscribing to Update responses...") update_accepted_subscribed_future, _ = shadow_client.subscribe_to_update_shadow_accepted( - request=iotshadow.UpdateShadowSubscriptionRequest(thing_name=args.thing_name), + request=iotshadow.UpdateShadowSubscriptionRequest(thing_name=shadow_thing_name), qos=mqtt.QoS.AT_LEAST_ONCE, callback=on_update_shadow_accepted) update_rejected_subscribed_future, _ = shadow_client.subscribe_to_update_shadow_rejected( - request=iotshadow.UpdateShadowSubscriptionRequest(thing_name=args.thing_name), + request=iotshadow.UpdateShadowSubscriptionRequest(thing_name=shadow_thing_name), qos=mqtt.QoS.AT_LEAST_ONCE, callback=on_update_shadow_rejected) @@ -358,12 +323,12 @@ def user_input_thread_fn(): print("Subscribing to Get responses...") get_accepted_subscribed_future, _ = shadow_client.subscribe_to_get_shadow_accepted( - request=iotshadow.GetShadowSubscriptionRequest(thing_name=args.thing_name), + request=iotshadow.GetShadowSubscriptionRequest(thing_name=shadow_thing_name), qos=mqtt.QoS.AT_LEAST_ONCE, callback=on_get_shadow_accepted) get_rejected_subscribed_future, _ = shadow_client.subscribe_to_get_shadow_rejected( - request=iotshadow.GetShadowSubscriptionRequest(thing_name=args.thing_name), + request=iotshadow.GetShadowSubscriptionRequest(thing_name=shadow_thing_name), qos=mqtt.QoS.AT_LEAST_ONCE, callback=on_get_shadow_rejected) @@ -373,7 +338,7 @@ def user_input_thread_fn(): print("Subscribing to Delta events...") delta_subscribed_future, _ = shadow_client.subscribe_to_shadow_delta_updated_events( - request=iotshadow.ShadowDeltaUpdatedSubscriptionRequest(thing_name=args.thing_name), + request=iotshadow.ShadowDeltaUpdatedSubscriptionRequest(thing_name=shadow_thing_name), qos=mqtt.QoS.AT_LEAST_ONCE, callback=on_shadow_delta_updated) @@ -392,7 +357,7 @@ def user_input_thread_fn(): token = str(uuid4()) publish_get_future = shadow_client.publish_get_shadow( - request=iotshadow.GetShadowRequest(thing_name=args.thing_name, client_token=token), + request=iotshadow.GetShadowRequest(thing_name=shadow_thing_name, client_token=token), qos=mqtt.QoS.AT_LEAST_ONCE) locked_data.request_tokens.add(token) diff --git a/samples/websocket_connect.py b/samples/websocket_connect.py new file mode 100644 index 00000000..f3d7c236 --- /dev/null +++ b/samples/websocket_connect.py @@ -0,0 +1,68 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0. + +from awscrt import http, auth +from awsiot import mqtt_connection_builder +from uuid import uuid4 + +# This sample shows how to create a MQTT connection using websockets. +# This sample is intended to be used as a reference for making MQTT connections. + +# Parse arguments +import command_line_utils +cmdUtils = command_line_utils.CommandLineUtils("PubSub - Send and recieve messages through an MQTT connection.") +cmdUtils.add_common_mqtt_commands() +cmdUtils.add_common_proxy_commands() +cmdUtils.add_common_logging_commands() +cmdUtils.register_command("signing_region", "", + "The signing region used for the websocket signer", + True, str) +cmdUtils.register_command("client_id", "", + "Client ID to use for MQTT connection (optional, default='test-*').", + default="test-" + str(uuid4())) +# Needs to be called so the command utils parse the commands +cmdUtils.get_args() + +# Callback when connection is accidentally lost. +def on_connection_interrupted(connection, error, **kwargs): + print("Connection interrupted. error: {}".format(error)) + +# Callback when an interrupted connection is re-established. +def on_connection_resumed(connection, return_code, session_present, **kwargs): + print("Connection resumed. return_code: {} session_present: {}".format(return_code, session_present)) + + +if __name__ == '__main__': + proxy_options = None + if cmdUtils.get_command(cmdUtils.m_cmd_proxy_host) and cmdUtils.get_command(cmdUtils.m_cmd_proxy_port): + proxy_options = http.HttpProxyOptions( + host_name=cmdUtils.get_command(cmdUtils.m_cmd_proxy_host), + port=cmdUtils.get_command(cmdUtils.m_cmd_proxy_port)) + + credentials_provider = auth.AwsCredentialsProvider.new_default_chain() + mqtt_connection = mqtt_connection_builder.websockets_with_default_aws_signing( + endpoint=cmdUtils.get_command_required(cmdUtils.m_cmd_endpoint), + region=cmdUtils.get_command_required("signing_region"), + credentials_provider=credentials_provider, + http_proxy_options=proxy_options, + ca_filepath=cmdUtils.get_command(cmdUtils.m_cmd_ca_file), + on_connection_interrupted=on_connection_interrupted, + on_connection_resumed=on_connection_resumed, + client_id=cmdUtils.get_command_required("client_id"), + clean_session=False, + keep_alive_secs=30) + + print("Connecting to {} with client ID '{}'...".format( + cmdUtils.get_command(cmdUtils.m_cmd_endpoint), cmdUtils.get_command("client_id"))) + + connect_future = mqtt_connection.connect() + + # Future.result() waits until a result is available + connect_future.result() + print("Connected!") + + # Disconnect + print("Disconnecting...") + disconnect_future = mqtt_connection.disconnect() + disconnect_future.result() + print("Disconnected!") From 623435bc58fa7c85dfd5c1ff4821499e6fcffb92 Mon Sep 17 00:00:00 2001 From: Noah Beard Date: Wed, 23 Mar 2022 12:15:03 -0400 Subject: [PATCH 06/10] Fixed codebuild tests to work with sample split --- codebuild/samples/{pubsub-linux.sh => connect-linux.sh} | 4 ++-- codebuild/samples/linux-smoke-tests.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename codebuild/samples/{pubsub-linux.sh => connect-linux.sh} (59%) diff --git a/codebuild/samples/pubsub-linux.sh b/codebuild/samples/connect-linux.sh similarity index 59% rename from codebuild/samples/pubsub-linux.sh rename to codebuild/samples/connect-linux.sh index 24d380b9..ab23b0a7 100755 --- a/codebuild/samples/pubsub-linux.sh +++ b/codebuild/samples/connect-linux.sh @@ -9,9 +9,9 @@ pushd $CODEBUILD_SRC_DIR/samples/ ENDPOINT=$(aws secretsmanager get-secret-value --secret-id "unit-test/endpoint" --query "SecretString" | cut -f2 -d":" | sed -e 's/[\\\"\}]//g') echo "Mqtt Direct test" -python3 pubsub.py --endpoint $ENDPOINT --key /tmp/privatekey.pem --cert /tmp/certificate.pem +python3 basic_connect.py --endpoint $ENDPOINT --key /tmp/privatekey.pem --cert /tmp/certificate.pem echo "Websocket test" -python3 pubsub.py --endpoint $ENDPOINT --use_websocket --signing_region us-east-1 +python3 websocket_connect.py --endpoint $ENDPOINT --signing_region us-east-1 popd diff --git a/codebuild/samples/linux-smoke-tests.yml b/codebuild/samples/linux-smoke-tests.yml index d1e1c35b..78261476 100644 --- a/codebuild/samples/linux-smoke-tests.yml +++ b/codebuild/samples/linux-smoke-tests.yml @@ -9,7 +9,7 @@ phases: commands: - echo Build started on `date` - $CODEBUILD_SRC_DIR/codebuild/samples/setup-linux.sh - - $CODEBUILD_SRC_DIR/codebuild/samples/pubsub-linux.sh + - $CODEBUILD_SRC_DIR/codebuild/samples/connect-linux.sh post_build: commands: - - echo Build completed on `date` \ No newline at end of file + - echo Build completed on `date` From ca2e0421935b882ee2a516df06d3978ba5639d98 Mon Sep 17 00:00:00 2001 From: Noah Beard Date: Thu, 24 Mar 2022 11:43:49 -0400 Subject: [PATCH 07/10] Adjusted code based on code review: * Added PubSub sample back to codebuild tests * Simplified connection samples by using utility functions in command_line_utils * Removed PKCS11 PubSub sample (since it's replaced by PKCS11 connect sample) * Added PKCS11 connection builder function to command_line_utils --- codebuild/.DS_Store | Bin 0 -> 6148 bytes codebuild/samples/linux-smoke-tests.yml | 1 + codebuild/samples/pubsub-linux.sh | 14 +++ samples/basic_connect.py | 24 +--- samples/command_line_utils.py | 32 +++++- samples/pkcs11_connect.py | 27 +---- samples/pkcs11_pubsub.py | 139 ------------------------ samples/websocket_connect.py | 24 +--- 8 files changed, 58 insertions(+), 203 deletions(-) create mode 100644 codebuild/.DS_Store create mode 100755 codebuild/samples/pubsub-linux.sh delete mode 100644 samples/pkcs11_pubsub.py diff --git a/codebuild/.DS_Store b/codebuild/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..deea664ca905e48a729f4038b86319e3237e47bb GIT binary patch literal 6148 zcmeHK%}T>S5T32oZYyFBg5qf}-g;=YiXMbe_25m2=s~4TXt9BqlqR)kt>iuQjeG!K z$C=%&sMU)Hkun3b-|YPCvR}gf0Dx!=+BJYO07z8AToIdZgyN)AlCvH{p~ncJ1BpLO zdVV;S&5r-b0DZdxB;dgf^x(tyi+zl|_`NVl(rWcps$LYj>C7`^Us3LuvuO?h@;dCqE25I2kj1~T%HF}J03RTLDWw4IIamu zS(L?Qd3`k6+}f#ERBd~Adt6bYz0GPx)wg!W+&|QOmN$ z2|T0mWudojKMLdM8Y7dcNj4)hzzi@0GtGeA^yK19uaXzX3@`(~VSx4rg-YmH%na(S z0~@+NQan#cf;PP+2$e<0VrCE{D8iH?no?n(7{ZjJU)emzVrI~kgU~bMICf@XUnoM) zj((-nK{y7vWd@jm&kW3&Zkf*iqo3dZpNqK13@`&T#em2kx`z#1l093e7Ds2TLT#av pP+n&6y#x(?6k{$O#cQZq(67iqbS!2D(SyPl0Zjup%)p;A@CF)eQ(yo9 literal 0 HcmV?d00001 diff --git a/codebuild/samples/linux-smoke-tests.yml b/codebuild/samples/linux-smoke-tests.yml index 78261476..928c6d79 100644 --- a/codebuild/samples/linux-smoke-tests.yml +++ b/codebuild/samples/linux-smoke-tests.yml @@ -10,6 +10,7 @@ phases: - echo Build started on `date` - $CODEBUILD_SRC_DIR/codebuild/samples/setup-linux.sh - $CODEBUILD_SRC_DIR/codebuild/samples/connect-linux.sh + - $CODEBUILD_SRC_DIR/codebuild/samples/pubsub-linux.sh post_build: commands: - echo Build completed on `date` diff --git a/codebuild/samples/pubsub-linux.sh b/codebuild/samples/pubsub-linux.sh new file mode 100755 index 00000000..18e44ad5 --- /dev/null +++ b/codebuild/samples/pubsub-linux.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +set -e + +env + +pushd $CODEBUILD_SRC_DIR/samples/ + +ENDPOINT=$(aws secretsmanager get-secret-value --secret-id "unit-test/endpoint" --query "SecretString" | cut -f2 -d":" | sed -e 's/[\\\"\}]//g') + +echo "PubSub test" +python3 pubsub.py --endpoint $ENDPOINT --key /tmp/privatekey.pem --cert /tmp/certificate.pem + +popd diff --git a/samples/basic_connect.py b/samples/basic_connect.py index f11423fa..d1c7b313 100644 --- a/samples/basic_connect.py +++ b/samples/basic_connect.py @@ -1,8 +1,6 @@ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0. -from awscrt import http -from awsiot import mqtt_connection_builder from uuid import uuid4 # This sample shows how to create a MQTT connection using a certificate file and key file. @@ -36,24 +34,10 @@ def on_connection_resumed(connection, return_code, session_present, **kwargs): if __name__ == '__main__': - proxy_options = None - if cmdUtils.get_command(cmdUtils.m_cmd_proxy_host) and cmdUtils.get_command(cmdUtils.m_cmd_proxy_port): - proxy_options = http.HttpProxyOptions( - host_name=cmdUtils.get_command(cmdUtils.m_cmd_proxy_host), - port=cmdUtils.get_command(cmdUtils.m_cmd_proxy_port)) - - mqtt_connection = mqtt_connection_builder.mtls_from_path( - endpoint=cmdUtils.get_command_required(cmdUtils.m_cmd_endpoint), - port=cmdUtils.get_command_required("port"), - cert_filepath=cmdUtils.get_command_required("cert"), - pri_key_filepath=cmdUtils.get_command_required("key"), - ca_filepath=cmdUtils.get_command(cmdUtils.m_cmd_ca_file), - on_connection_interrupted=on_connection_interrupted, - on_connection_resumed=on_connection_resumed, - client_id=cmdUtils.get_command_required("client_id"), - clean_session=False, - keep_alive_secs=30, - http_proxy_options=proxy_options) + # Create a connection using a certificate and key. + # Note: The data for the connection is gotten from cmdUtils. + # (see build_direct_mqtt_connection for implementation) + mqtt_connection = cmdUtils.build_direct_mqtt_connection(on_connection_interrupted, on_connection_resumed) print("Connecting to {} with client ID '{}'...".format( cmdUtils.get_command(cmdUtils.m_cmd_endpoint), cmdUtils.get_command("client_id"))) diff --git a/samples/command_line_utils.py b/samples/command_line_utils.py index 6b7b33ba..25a4a20e 100644 --- a/samples/command_line_utils.py +++ b/samples/command_line_utils.py @@ -98,6 +98,36 @@ def get_command(self, command_name, default=None): return getattr(self.parsed_commands, command_name) return default + def build_pkcs11_mqtt_connection(self, on_connection_interrupted, on_connection_resumed): + + pkcs11_lib_path = self.get_command_required(self.m_cmd_pkcs11_lib) + print(f"Loading PKCS#11 library '{pkcs11_lib_path}' ...") + pkcs11_lib = io.Pkcs11Lib( + file=pkcs11_lib_path, + behavior=io.Pkcs11Lib.InitializeFinalizeBehavior.STRICT) + print("Loaded!") + + pkcs11_slot_id = None + if (self.get_command(self.m_cmd_pkcs11_slot) != None): + pkcs11_slot_id = int(self.get_command(self.m_cmd_pkcs11_slot)) + + # Create MQTT connection + mqtt_connection = mqtt_connection_builder.mtls_with_pkcs11( + pkcs11_lib=pkcs11_lib, + user_pin=self.get_command_required(self.m_cmd_pkcs11_pin), + slot_id=pkcs11_slot_id, + token_label=self.get_command_required(self.m_cmd_pkcs11_token), + private_key_label=self.get_command_required(self.m_cmd_pkcs11_key), + cert_filepath=self.get_command_required(self.m_cmd_pkcs11_cert), + endpoint=self.get_command_required(self.m_cmd_endpoint), + port=self.get_command("port"), + ca_filepath=self.get_command(self.m_cmd_ca_file), + on_connection_interrupted=on_connection_interrupted, + on_connection_resumed=on_connection_resumed, + client_id=self.get_command_required("client_id"), + clean_session=False, + keep_alive_secs=30) + def build_websocket_mqtt_connection(self, on_connection_interrupted, on_connection_resumed): proxy_options = self.get_proxy_options_for_mqtt_connection() credentials_provider = auth.AwsCredentialsProvider.new_default_chain() @@ -153,7 +183,7 @@ def get_proxy_options_for_mqtt_connection(self): m_cmd_signing_region = "signing_region" m_cmd_pkcs11_lib = "pkcs11_lib" m_cmd_pkcs11_cert = "cert" - m_cmd_pkcs11_pin = "pkcs11_pin" + m_cmd_pkcs11_pin = "pin" m_cmd_pkcs11_token = "token_label" m_cmd_pkcs11_slot = "slot_id" m_cmd_pkcs11_key = "key_label" diff --git a/samples/pkcs11_connect.py b/samples/pkcs11_connect.py index 7d3b9da1..2587594b 100644 --- a/samples/pkcs11_connect.py +++ b/samples/pkcs11_connect.py @@ -45,29 +45,10 @@ def on_connection_resumed(connection, return_code, session_present, **kwargs): if __name__ == '__main__': - pkcs11_lib_file = cmdUtils.get_command_required("pkcs11_lib") - print(f"Loading PKCS#11 library '{pkcs11_lib_file}' ...") - pkcs11_lib = io.Pkcs11Lib( - file=pkcs11_lib_file, - behavior=io.Pkcs11Lib.InitializeFinalizeBehavior.STRICT) - print("Loaded!") - - # Create MQTT connection - mqtt_connection = mqtt_connection_builder.mtls_with_pkcs11( - pkcs11_lib=pkcs11_lib, - user_pin=cmdUtils.get_command_required("pin"), - slot_id=cmdUtils.get_command("slot_id"), - token_label=cmdUtils.get_command("token_label"), - private_key_label=cmdUtils.get_command("key_label"), - cert_filepath=cmdUtils.get_command_required("cert"), - endpoint=cmdUtils.get_command_required(cmdUtils.m_cmd_endpoint), - port=cmdUtils.get_command("port"), - ca_filepath=cmdUtils.get_command(cmdUtils.m_cmd_ca_file), - on_connection_interrupted=on_connection_interrupted, - on_connection_resumed=on_connection_resumed, - client_id=cmdUtils.get_command_required("client_id"), - clean_session=False, - keep_alive_secs=30) + # Create a connection using websockets. + # Note: The data for the connection is gotten from cmdUtils. + # (see build_websocket_mqtt_connection for implementation) + mqtt_connection = cmdUtils.build_pkcs11_mqtt_connection(on_connection_interrupted, on_connection_resumed) print("Connecting to {} with client ID '{}'...".format( cmdUtils.get_command("endpoint"), cmdUtils.get_command("client_id"))) diff --git a/samples/pkcs11_pubsub.py b/samples/pkcs11_pubsub.py deleted file mode 100644 index ff64eca0..00000000 --- a/samples/pkcs11_pubsub.py +++ /dev/null @@ -1,139 +0,0 @@ -# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -# SPDX-License-Identifier: Apache-2.0. - -import argparse -from awscrt import io, mqtt -from awsiot import mqtt_connection_builder -import sys -import threading -import time -from uuid import uuid4 -import json - -# This sample is similar to `samples/pubsub.py` but the private key -# for mutual TLS is stored on a PKCS#11 compatible smart card or -# Hardware Security Module (HSM). -# -# See `samples/README.md` for instructions on setting up your PKCS#11 device -# to run this sample. -# -# WARNING: Unix only. Currently, TLS integration with PKCS#11 is only available on Unix devices. - -# Parse arguments -import command_line_utils; -cmdUtils = command_line_utils.CommandLineUtils("PKCS#11 PubSub - Send and recieve messages through an MQTT connection.") -cmdUtils.add_common_mqtt_commands() -cmdUtils.add_common_topic_message_commands() -cmdUtils.add_common_logging_commands() -cmdUtils.register_command("port", "", "Connection port. AWS IoT supports 433 and 8883 (optional, default=auto).", type=int) -cmdUtils.register_command("pkcs11_lib", "", "Path to PKCS#11 Library", required=True) -cmdUtils.register_command("pin", "", "User PIN for logging into PKCS#11 token.", required=True) -cmdUtils.register_command("token_label", "", "Label of the PKCS#11 token to use (optional).") -cmdUtils.register_command("slot_id", "", "Slot ID containing the PKCS#11 token to use (optional).") -cmdUtils.register_command("key_label", "", "Label of private key on the PKCS#11 token (optional).") -cmdUtils.register_command("client_id", "", "Client ID to use for MQTT connection (optional, default='test-*').", default="test-" + str(uuid4())) -cmdUtils.register_command("count", "", "The number of messages to send (optional, default='10').", default=10, type=int) -args = cmdUtils.get_args() - -io.init_logging(getattr(io.LogLevel, args.verbosity), 'stderr') - -received_count = 0 -received_all_event = threading.Event() - - -def on_connection_interrupted(connection, error, **kwargs): - # Callback when connection is accidentally lost. - print("Connection interrupted. error: {}".format(error)) - - -def on_connection_resumed(connection, return_code, session_present, **kwargs): - # Callback when an interrupted connection is re-established. - print("Connection resumed. return_code: {} session_present: {}".format(return_code, session_present)) - - -# Callback when the subscribed topic receives a message -def on_message_received(topic, payload, dup, qos, retain, **kwargs): - print("Received message from topic '{}': {}".format(topic, payload)) - global received_count - received_count += 1 - if received_count == args.count: - received_all_event.set() - - -if __name__ == '__main__': - print(f"Loading PKCS#11 library '{args.pkcs11_lib}' ...") - pkcs11_lib = io.Pkcs11Lib( - file=args.pkcs11_lib, - behavior=io.Pkcs11Lib.InitializeFinalizeBehavior.STRICT) - print("Loaded!") - - # Create MQTT connection - mqtt_connection = mqtt_connection_builder.mtls_with_pkcs11( - pkcs11_lib=pkcs11_lib, - user_pin=args.pin, - slot_id=int(args.slot_id) if args.slot_id else None, - token_label=args.token_label, - private_key_label=args.key_label, - cert_filepath=args.cert, - endpoint=args.endpoint, - port=args.port, - ca_filepath=args.ca_file, - on_connection_interrupted=on_connection_interrupted, - on_connection_resumed=on_connection_resumed, - client_id=args.client_id, - clean_session=False, - keep_alive_secs=30) - - print("Connecting to {} with client ID '{}'...".format( - args.endpoint, args.client_id)) - - connect_future = mqtt_connection.connect() - - # Future.result() waits until a result is available - connect_future.result() - print("Connected!") - - # Subscribe - print("Subscribing to topic '{}'...".format(args.topic)) - subscribe_future, packet_id = mqtt_connection.subscribe( - topic=args.topic, - qos=mqtt.QoS.AT_LEAST_ONCE, - callback=on_message_received) - - subscribe_result = subscribe_future.result() - print("Subscribed with {}".format(str(subscribe_result['qos']))) - - # Publish message to server desired number of times. - # This step is skipped if message is blank. - # This step loops forever if count was set to 0. - if args.message: - if args.count == 0: - print("Sending messages until program killed") - else: - print("Sending {} message(s)".format(args.count)) - - publish_count = 1 - while (publish_count <= args.count) or (args.count == 0): - message = "{} [{}]".format(args.message, publish_count) - print("Publishing message to topic '{}': {}".format(args.topic, message)) - message_json = json.dumps(message) - mqtt_connection.publish( - topic=args.topic, - payload=message_json, - qos=mqtt.QoS.AT_LEAST_ONCE) - time.sleep(1) - publish_count += 1 - - # Wait for all messages to be received. - # This waits forever if count was set to 0. - if args.count != 0 and not received_all_event.is_set(): - print("Waiting for all messages to be received...") - - received_all_event.wait() - print("{} message(s) received.".format(received_count)) - - # Disconnect - print("Disconnecting...") - disconnect_future = mqtt_connection.disconnect() - disconnect_future.result() - print("Disconnected!") diff --git a/samples/websocket_connect.py b/samples/websocket_connect.py index f3d7c236..649e72ad 100644 --- a/samples/websocket_connect.py +++ b/samples/websocket_connect.py @@ -1,8 +1,6 @@ # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: Apache-2.0. -from awscrt import http, auth -from awsiot import mqtt_connection_builder from uuid import uuid4 # This sample shows how to create a MQTT connection using websockets. @@ -33,24 +31,10 @@ def on_connection_resumed(connection, return_code, session_present, **kwargs): if __name__ == '__main__': - proxy_options = None - if cmdUtils.get_command(cmdUtils.m_cmd_proxy_host) and cmdUtils.get_command(cmdUtils.m_cmd_proxy_port): - proxy_options = http.HttpProxyOptions( - host_name=cmdUtils.get_command(cmdUtils.m_cmd_proxy_host), - port=cmdUtils.get_command(cmdUtils.m_cmd_proxy_port)) - - credentials_provider = auth.AwsCredentialsProvider.new_default_chain() - mqtt_connection = mqtt_connection_builder.websockets_with_default_aws_signing( - endpoint=cmdUtils.get_command_required(cmdUtils.m_cmd_endpoint), - region=cmdUtils.get_command_required("signing_region"), - credentials_provider=credentials_provider, - http_proxy_options=proxy_options, - ca_filepath=cmdUtils.get_command(cmdUtils.m_cmd_ca_file), - on_connection_interrupted=on_connection_interrupted, - on_connection_resumed=on_connection_resumed, - client_id=cmdUtils.get_command_required("client_id"), - clean_session=False, - keep_alive_secs=30) + # Create a connection using websockets. + # Note: The data for the connection is gotten from cmdUtils. + # (see build_websocket_mqtt_connection for implementation) + mqtt_connection = cmdUtils.build_websocket_mqtt_connection(on_connection_interrupted, on_connection_resumed) print("Connecting to {} with client ID '{}'...".format( cmdUtils.get_command(cmdUtils.m_cmd_endpoint), cmdUtils.get_command("client_id"))) From 75764fcff83ebf4a232bc9bdc6349e2673c39c97 Mon Sep 17 00:00:00 2001 From: Noah Beard Date: Thu, 24 Mar 2022 11:59:31 -0400 Subject: [PATCH 08/10] Converted Windows Cert PubSub to Windows Cert Connect Also adjusted the README accordingly and fixed the connect samples using the wrong titles. --- samples/README.md | 66 ++++++++++++++-- samples/basic_connect.py | 2 +- samples/pkcs11_connect.py | 2 +- samples/websocket_connect.py | 2 +- samples/windows_cert_connect.py | 71 ++++++++++++++++++ samples/windows_cert_pubsub.py | 129 -------------------------------- 6 files changed, 133 insertions(+), 139 deletions(-) create mode 100644 samples/windows_cert_connect.py delete mode 100644 samples/windows_cert_pubsub.py diff --git a/samples/README.md b/samples/README.md index 7da14cc4..d3163cac 100644 --- a/samples/README.md +++ b/samples/README.md @@ -1,8 +1,10 @@ # Sample apps for the AWS IoT Device SDK v2 for Python * [PubSub](#pubsub) -* [PKCS#11 PubSub](#pkcs11-pubsub) -* [Windows Certificate PubSub](#windows-certificate-pubsub) +* [Basic Connect](#basic-connect) +* [Websocket Connect](#websocket-connect) +* [PKCS#11 Connect](#pkcs11-connect) +* [Windows Certificate Connect](#windows-certificate-connect) * [Shadow](#shadow) * [Jobs](#jobs) * [Fleet Provisioning](#fleet-provisioning) @@ -156,7 +158,7 @@ python3 websocket_connect.py --endpoint --ca_file --signing_re Note that using Websockets will attempt to fetch the AWS credentials from your enviornment variables or local files. See the [authorizing direct AWS](https://docs.aws.amazon.com/iot/latest/developerguide/authorizing-direct-aws.html) page for documentation on how to get the AWS credentials, which then you can set to the `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS`, and `AWS_SESSION_TOKEN` environment variables. -## PKCS#11 PubSub +## PKCS#11 Connect This sample is similar to the [Basic Connect](#basic-connect), but the private key for mutual TLS is stored on a PKCS#11 compatible smart card or Hardware Security Module (HSM) @@ -165,6 +167,31 @@ WARNING: Unix only. Currently, TLS integration with PKCS#11 is only available on source: `samples/pkcs11_connect.py` +Your Thing's +[Policy](https://docs.aws.amazon.com/iot/latest/developerguide/iot-policies.html) +must provide privileges for this sample to connect, subscribe, publish, +and receive. + +
+(see sample policy) +
+{
+  "Version": "2012-10-17",
+  "Statement": [
+    {
+      "Effect": "Allow",
+      "Action": [
+        "iot:Connect"
+      ],
+      "Resource": [
+        "arn:aws:iot:region:account:client/test-*"
+      ]
+    }
+  ]
+}
+
+
+ To run this sample using [SoftHSM2](https://www.opendnssec.org/softhsm/) as the PKCS#11 device: 1) Create an IoT Thing with a certificate and key if you haven't already. @@ -209,11 +236,11 @@ To run this sample using [SoftHSM2](https://www.opendnssec.org/softhsm/) as the python3 pkcs11_connect.py --endpoint --ca_file --cert --pkcs11_lib --pin --token_label --key_label ``` -## Windows Certificate PubSub +## Windows Certificate Connect WARNING: Windows only -This sample is similar to the basic [PubSub](#pubsub), +This sample is similar to the basic [Connect](#basic-connect), but your certificate and private key are in a [Windows certificate store](https://docs.microsoft.com/en-us/windows-hardware/drivers/install/certificate-stores), rather than simply being files on disk. @@ -227,7 +254,32 @@ If your certificate and private key are in a [TPM](https://docs.microsoft.com/en-us/windows/security/information-protection/tpm/trusted-platform-module-overview),, you would use them by passing their certificate store path. -source: `samples/windows_cert_pubsub.py` +source: `samples/windows_cert_connect.py` + +Your Thing's +[Policy](https://docs.aws.amazon.com/iot/latest/developerguide/iot-policies.html) +must provide privileges for this sample to connect, subscribe, publish, +and receive. + +
+(see sample policy) +
+{
+  "Version": "2012-10-17",
+  "Statement": [
+    {
+      "Effect": "Allow",
+      "Action": [
+        "iot:Connect"
+      ],
+      "Resource": [
+        "arn:aws:iot:region:account:client/test-*"
+      ]
+    }
+  ]
+}
+
+
To run this sample with a basic certificate from AWS IoT Core: @@ -269,7 +321,7 @@ To run this sample with a basic certificate from AWS IoT Core: 4) Now you can run the sample: ```sh - python3 windows_cert_pubsub.py --endpoint xxxx-ats.iot.xxxx.amazonaws.com --root-ca AmazonRootCA.pem --cert CurrentUser\My\A11F8A9B5DF5B98BA3508FBCA575D09570E0D2C6 + python3 windows_cert_connect.py --endpoint xxxx-ats.iot.xxxx.amazonaws.com --ca_file AmazonRootCA.pem --cert CurrentUser\My\A11F8A9B5DF5B98BA3508FBCA575D09570E0D2C6 ``` ## Shadow diff --git a/samples/basic_connect.py b/samples/basic_connect.py index d1c7b313..58b7855a 100644 --- a/samples/basic_connect.py +++ b/samples/basic_connect.py @@ -8,7 +8,7 @@ # Parse arguments import command_line_utils -cmdUtils = command_line_utils.CommandLineUtils("PubSub - Send and recieve messages through an MQTT connection.") +cmdUtils = command_line_utils.CommandLineUtils("Basic Connect - Make a MQTT connection.") cmdUtils.add_common_mqtt_commands() cmdUtils.add_common_proxy_commands() cmdUtils.add_common_logging_commands() diff --git a/samples/pkcs11_connect.py b/samples/pkcs11_connect.py index 2587594b..267bb84f 100644 --- a/samples/pkcs11_connect.py +++ b/samples/pkcs11_connect.py @@ -16,7 +16,7 @@ # Parse arguments import command_line_utils -cmdUtils = command_line_utils.CommandLineUtils("PubSub - Send and recieve messages through an MQTT connection.") +cmdUtils = command_line_utils.CommandLineUtils("PKCS11 Connect - Make a MQTT connection using PKCS11.") cmdUtils.add_common_mqtt_commands() cmdUtils.add_common_proxy_commands() cmdUtils.add_common_logging_commands() diff --git a/samples/websocket_connect.py b/samples/websocket_connect.py index 649e72ad..2e6d3418 100644 --- a/samples/websocket_connect.py +++ b/samples/websocket_connect.py @@ -8,7 +8,7 @@ # Parse arguments import command_line_utils -cmdUtils = command_line_utils.CommandLineUtils("PubSub - Send and recieve messages through an MQTT connection.") +cmdUtils = command_line_utils.CommandLineUtils("Websocket Connect - Make a websocket MQTT connection.") cmdUtils.add_common_mqtt_commands() cmdUtils.add_common_proxy_commands() cmdUtils.add_common_logging_commands() diff --git a/samples/windows_cert_connect.py b/samples/windows_cert_connect.py new file mode 100644 index 00000000..d9a2f3a8 --- /dev/null +++ b/samples/windows_cert_connect.py @@ -0,0 +1,71 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0. + +from awsiot import mqtt_connection_builder +from uuid import uuid4 + +# This sample is similar to `samples/basic_connect.py` but the certificate +# for mutual TLS is stored in a Windows certificate store. +# +# See `samples/README.md` for instructions on setting up your PC +# to run this sample. +# +# WARNING: Windows only. + +# Parse arguments +import command_line_utils +cmdUtils = command_line_utils.CommandLineUtils("Windows Cert Connect - Make a MQTT connection using Windows Store Certificates.") +cmdUtils.add_common_mqtt_commands() +cmdUtils.add_common_logging_commands() +cmdUtils.register_command("port", "", + "Connection port for direct connection. " + + "AWS IoT supports 433 and 8883 (optional, default=8883).", + False, int) +cmdUtils.register_command("client_id", "", + "Client ID to use for MQTT connection (optional, default='test-*').", + default="test-" + str(uuid4())) +cmdUtils.register_command("cert", "", + "Path to certificate in Windows certificate store. " + + "e.g. \"CurrentUser\\MY\\6ac133ac58f0a88b83e9c794eba156a98da39b4c\"", + True, str) +# Needs to be called so the command utils parse the commands +cmdUtils.get_args() + + +def on_connection_interrupted(connection, error, **kwargs): + # Callback when connection is accidentally lost. + print("Connection interrupted. error: {}".format(error)) + + +def on_connection_resumed(connection, return_code, session_present, **kwargs): + # Callback when an interrupted connection is re-established. + print("Connection resumed. return_code: {} session_present: {}".format(return_code, session_present)) + + +if __name__ == '__main__': + # Create MQTT connection + mqtt_connection = mqtt_connection_builder.mtls_with_windows_cert_store_path( + cert_store_path=cmdUtils.get_command_required("cert"), + endpoint=cmdUtils.get_command_required(cmdUtils.m_cmd_endpoint), + port=cmdUtils.get_command("port"), + ca_filepath=cmdUtils.get_command(cmdUtils.m_cmd_ca_file), + on_connection_interrupted=on_connection_interrupted, + on_connection_resumed=on_connection_resumed, + client_id=cmdUtils.get_command("client_id"), + clean_session=False, + keep_alive_secs=30) + + print("Connecting to {} with client ID '{}'...".format( + cmdUtils.get_command(cmdUtils.m_cmd_endpoint), cmdUtils.get_command("client_id"))) + + connect_future = mqtt_connection.connect() + + # Future.result() waits until a result is available + connect_future.result() + print("Connected!") + + # Disconnect + print("Disconnecting...") + disconnect_future = mqtt_connection.disconnect() + disconnect_future.result() + print("Disconnected!") diff --git a/samples/windows_cert_pubsub.py b/samples/windows_cert_pubsub.py deleted file mode 100644 index f1e873b7..00000000 --- a/samples/windows_cert_pubsub.py +++ /dev/null @@ -1,129 +0,0 @@ -# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -# SPDX-License-Identifier: Apache-2.0. - -import argparse -from awscrt import io, mqtt -from awsiot import mqtt_connection_builder -import threading -import time -from uuid import uuid4 - -# This sample is similar to `samples/pubsub.py` but the certificate -# for mutual TLS is stored in a Windows certificate store. -# -# See `samples/README.md` for instructions on setting up your PC -# to run this sample. -# -# WARNING: Windows only. - -parser = argparse.ArgumentParser(description="Send and receive messages through and MQTT connection.") -parser.add_argument('--endpoint', required=True, help="Your AWS IoT custom endpoint, not including a port. " + - "e.g. \"abcd123456wxyz-ats.iot.us-east-1.amazonaws.com\"") -parser.add_argument('--port', type=int, help="Specify port. AWS IoT supports 443 and 8883. (default: auto)") -parser.add_argument('--cert', required=True, help="Path to certificate in Windows certificate store. " + - "e.g. \"CurrentUser\\MY\\6ac133ac58f0a88b83e9c794eba156a98da39b4c\"") -parser.add_argument('--root-ca', help="File path to root certificate authority, in PEM format. (default: None)") -parser.add_argument('--client-id', default="test-" + str(uuid4()), - help="Client ID for MQTT connection. (default: 'test-*')") -parser.add_argument('--topic', default="test/topic", - help="Topic to subscribe to, and publish messages to. (default: 'test/topic')") -parser.add_argument('--message', default="Hello World!", - help="Message to publish. Specify empty string to publish nothing. (default: 'Hello World!')") -parser.add_argument('--count', default=10, type=int, help="Number of messages to publish/receive before exiting. " + - "Specify 0 to run forever. (default: 10)") -parser.add_argument('--verbosity', choices=[x.name for x in io.LogLevel], default=io.LogLevel.Error.name, - help="Logging level. (default: 'Error')") - -# Using globals to simplify sample code -args = parser.parse_args() - -io.init_logging(getattr(io.LogLevel, args.verbosity), 'stderr') - -received_count = 0 -received_all_event = threading.Event() - - -def on_connection_interrupted(connection, error, **kwargs): - # Callback when connection is accidentally lost. - print("Connection interrupted. error: {}".format(error)) - - -def on_connection_resumed(connection, return_code, session_present, **kwargs): - # Callback when an interrupted connection is re-established. - print("Connection resumed. return_code: {} session_present: {}".format(return_code, session_present)) - - -# Callback when the subscribed topic receives a message -def on_message_received(topic, payload, dup, qos, retain, **kwargs): - print("Received message from topic '{}': {}".format(topic, payload)) - global received_count - received_count += 1 - if received_count == args.count: - received_all_event.set() - - -if __name__ == '__main__': - # Create MQTT connection - mqtt_connection = mqtt_connection_builder.mtls_with_windows_cert_store_path( - cert_store_path=args.cert, - endpoint=args.endpoint, - port=args.port, - ca_filepath=args.root_ca, - on_connection_interrupted=on_connection_interrupted, - on_connection_resumed=on_connection_resumed, - client_id=args.client_id, - clean_session=False, - keep_alive_secs=30) - - print("Connecting to {} with client ID '{}'...".format( - args.endpoint, args.client_id)) - - connect_future = mqtt_connection.connect() - - # Future.result() waits until a result is available - connect_future.result() - print("Connected!") - - # Subscribe - print("Subscribing to topic '{}'...".format(args.topic)) - subscribe_future, packet_id = mqtt_connection.subscribe( - topic=args.topic, - qos=mqtt.QoS.AT_LEAST_ONCE, - callback=on_message_received) - - subscribe_result = subscribe_future.result() - print("Subscribed with {}".format(str(subscribe_result['qos']))) - - # Publish message to server desired number of times. - # This step is skipped if message is blank. - # This step loops forever if count was set to 0. - if args.message: - if args.count == 0: - print("Sending messages until program killed") - else: - print("Sending {} message(s)".format(args.count)) - - publish_count = 1 - while (publish_count <= args.count) or (args.count == 0): - message = "{} [{}]".format(args.message, publish_count) - print("Publishing message to topic '{}': {}".format(args.topic, message)) - mqtt_connection.publish( - topic=args.topic, - payload=message, - qos=mqtt.QoS.AT_LEAST_ONCE) - time.sleep(1) - publish_count += 1 - - # Wait for all messages to be received. - # This waits forever if count was set to 0. - if args.count != 0 and not received_all_event.is_set(): - print("Waiting for all messages to be received...") - - received_all_event.wait() - print("{} message(s) received.".format(received_count)) - - # Disconnect - print("Disconnecting...") - disconnect_future = mqtt_connection.disconnect() - disconnect_future.result() - print("Disconnected!") From 4163ca7327dfd31ef5f58b7f1faca015627c322f Mon Sep 17 00:00:00 2001 From: Noah Beard Date: Fri, 1 Apr 2022 14:39:05 -0400 Subject: [PATCH 09/10] Adjusted README to use consistent command format --- samples/README.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/samples/README.md b/samples/README.md index d3163cac..77772589 100644 --- a/samples/README.md +++ b/samples/README.md @@ -233,7 +233,7 @@ To run this sample using [SoftHSM2](https://www.opendnssec.org/softhsm/) as the 5) Now you can run the sample: ```sh - python3 pkcs11_connect.py --endpoint --ca_file --cert --pkcs11_lib --pin --token_label --key_label + python3 pkcs11_connect.py --endpoint --ca_file --cert --pkcs11_lib --pin --token_label --key_label ``` ## Windows Certificate Connect @@ -321,7 +321,7 @@ To run this sample with a basic certificate from AWS IoT Core: 4) Now you can run the sample: ```sh - python3 windows_cert_connect.py --endpoint xxxx-ats.iot.xxxx.amazonaws.com --ca_file AmazonRootCA.pem --cert CurrentUser\My\A11F8A9B5DF5B98BA3508FBCA575D09570E0D2C6 + python3 windows_cert_connect.py --endpoint --ca_file --cert ``` ## Shadow @@ -626,11 +626,11 @@ and `--key` appropriately: ``` sh python3 fleetprovisioning.py \ - --endpoint [your endpoint]-ats.iot.[region].amazonaws.com \ - --ca_file [pathToRootCA] \ - --cert /tmp/provision.cert.pem \ - --key /tmp/provision.private.key \ - --template_name [TemplateName] \ + --endpoint \ + --ca_file \ + --cert \ + --key \ + --template_name