Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions samples/python/command-response/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ It will listen for commands by subscribing to topics ending in `/cmd` and acknow
by sending a response.
The script will terminate when it receives a _shutdown_ command or a keyboard interrupt (Control-C).

Notes:

- The MQTT client must connect with `clean_session` set to false. This informs the
IoT Platform that the device accepts commands. The platform will also ensure messages
are retained while the device is disconnected.
- The command handler runs in a separate thread so that the on_message handler returns
quickly and does not block the MQTT client loop.

## Prerequisites

Install the Python dependencies
Expand Down
49 changes: 30 additions & 19 deletions samples/python/command-response/command-response.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import json
import os
import queue
import sys
import threading
import time
Expand Down Expand Up @@ -39,12 +40,13 @@ def on_connect(client, userdata, flags, reason_code, properties=None):
client.subscribe("#", qos=config.qos)


# Callback for MQTT message received event.
def on_message(client, userdata, message, properties=None):
topic = message.topic
payload = message.payload.decode()
state = userdata
if topic.endswith("/cmd"):
# Command handler thread
def command_handler():
while True:
try:
topic, payload, state = command_handler_queue.get()
except queue.ShutDown:
break
print(f"Received command on {topic}: {payload}")
# Command handling logic goes here.
# For this example, only the shutdown command is handled, which will
Expand All @@ -63,22 +65,36 @@ def on_message(client, userdata, message, properties=None):
ack_msg = json.dumps(
{"status": "acknowledged", "time": current_epoch_microseconds()}
)
state["ack_msg_info"].append(
client.publish(topic=rsp_topic, payload=ack_msg, qos=config.qos)
)
rc_pub = client.publish(topic=rsp_topic, payload=ack_msg, qos=config.qos)
rc_pub.wait_for_publish()
print(f"Finished command handling for: {topic}")
command_handler_queue.task_done()


# Callback for MQTT message received event.
def on_message(client, userdata, message, properties=None):
topic = message.topic
payload = message.payload.decode()
state = userdata
if topic.endswith("/cmd"):
command_handler_queue.put((topic, payload, state))


if config.auth_type not in ("basic", "cert"):
raise ValueError("auth_type must be 'basic' or 'cert'")

# Initialize queue and start command handler thread
command_handler_queue = queue.Queue()
command_handler_thread = threading.Thread(target=command_handler)
command_handler_thread.start()

client = mqtt.Client(
client_id=config.client_id, # Ensure client_id is set for persistent sessions.
clean_session=False, # Enable persistent session.
protocol=mqtt.MQTTv311, # Use v311 unless v5 features are needed.
callback_api_version=mqtt.CallbackAPIVersion.VERSION2, # type: ignore
)
state = {
"ack_msg_info": [],
"shutdown_event": threading.Event(),
}
client.user_data_set(state)
Expand Down Expand Up @@ -121,19 +137,14 @@ def on_message(client, userdata, message, properties=None):
rc_pub.wait_for_publish()
count += 1
time.sleep(config.message_delay)
# Drain ack_msg_info
while state["ack_msg_info"]:
state["ack_msg_info"].pop(0).wait_for_publish()

except KeyboardInterrupt:
print("\nInterrupted by user. Exiting...")

# Wait to process any potential /cmd messages before exit.
print("Waiting 2 seconds to process possible /cmd messages...")
time.sleep(2)
# Drain ack_msg_info
while state["ack_msg_info"]:
state["ack_msg_info"].pop(0).wait_for_publish()
# Wait for the queue to drain
print("Waiting for commands to be processed...")
command_handler_queue.shutdown()
command_handler_thread.join()

# Tear down the client and exit.
client.loop_stop()
Expand Down
2 changes: 1 addition & 1 deletion samples/python/manage-dt/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ Options:
-d, --debug Debug mode
--profile TEXT The profile in the config file to load.
[default: wim-iot-fra]
--auth [api_key|instance_principal|resource_principal]
--auth [api_key|instance_principal|resource_principal|security_token]
The type of auth to use for the API request.
[default: api_key]
--data-dir DIRECTORY Data directory [default: ./data]
Expand Down
3 changes: 0 additions & 3 deletions samples/python/manage-dt/manage_dt/mdt_oci.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,6 @@ def get_oci_config(
case "security_token":
logger.debug("OCI authentication: Session Token")
config = oci_config.from_file(profile_name=profile)
if os.getenv("OCI_CLI_TENANCY"):
logger.debug("Overriding tenancy OCID")
config["tenancy"] = os.getenv("OCI_CLI_TENANCY")
token_file = config["security_token_file"]
token = None
with open(token_file, "r") as f:
Expand Down
2 changes: 1 addition & 1 deletion samples/python/query-db/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ Copy `config.distr.py` to `config.py` and set the following variables:
- `db_token_scope`: The dbTokenScope property of your IoT Domain Group.
- `iot_domain_short_name`: The hostname part of the deviceHost property of your IoT Domain.
- `oci_auth_type`: The OCI Authentication type. Must be either "ConfigFileAuthentication"
for API Key authentication or "InstancePrincipal".
for API Key authentication, "InstancePrincipal" or "SecurityToken".
- `oci_profile`: OCI CLI profile to use for token retrieval when using API Key authentication.
- `row_count`: The number of rows retrieved by the sample queries.
- `thick_mode`: Set to `True` to use the `oracledb` Thick mode driver.
Expand Down
3 changes: 2 additions & 1 deletion samples/python/query-db/config.distr.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
# jq -r '.'data."device-host"' | split(".")[0]'
iot_domain_short_name = "<Domain SHort Name>"

# OCI Authentication type. Must be either "ConfigFileAuthentication" or "InstancePrincipal"
# OCI Authentication type. Must be either "ConfigFileAuthentication", "InstancePrincipal"
# or "SecurityToken"
# oci_auth_type = "ConfigFileAuthentication"
oci_auth_type = "InstancePrincipal"

Expand Down
2 changes: 1 addition & 1 deletion samples/python/query-db/query_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ def get_blob(blob: oracledb.LOB) -> Tuple[str, str]:
"auth_type": config.oci_auth_type,
"scope": config.db_token_scope,
}
if config.oci_auth_type == "ConfigFileAuthentication":
if config.oci_auth_type in ["ConfigFileAuthentication", "SecurityToken"]:
token_based_auth["profile"] = config.oci_profile

extra_connect_params = {}
Expand Down
4 changes: 2 additions & 2 deletions samples/python/queues/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ Copy `config.distr.py` to `config.py` and set the following variables:
- `db_connect_string`: The `dbConnectionString` property of your IoT Domain Group.
- `db_token_scope`: The `dbTokenScope` property of your IoT Domain Group.
- `iot_domain_short_name`: The hostname part of the `deviceHost` property of your IoT Domain.
- `oci_auth_type`: The OCI authentication type. Use "ConfigFileAuthentication"
for API key authentication, or "InstancePrincipal".
- `oci_auth_type`: The OCI Authentication type. Must be either "ConfigFileAuthentication"
for API Key authentication, "InstancePrincipal" or "SecurityToken".
- `oci_profile`: OCI CLI profile to use for token retrieval (API key authentication only).
- `thick_mode`: Set to `True` to use the `oracledb` thick mode driver.
- `subscriber_name`: Name of the durable subscriber for the `sub-norm` sample.
Expand Down
3 changes: 2 additions & 1 deletion samples/python/queues/config.distr.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
# jq -r '.'data."device-host"' | split(".")[0]'
iot_domain_short_name = "<Domain SHort Name>"

# OCI Authentication type. Must be either "ConfigFileAuthentication" or "InstancePrincipal"
# OCI Authentication type. Must be either "ConfigFileAuthentication", "InstancePrincipal"
# or "SecurityToken"
# oci_auth_type = "ConfigFileAuthentication"
oci_auth_type = "InstancePrincipal"

Expand Down
2 changes: 1 addition & 1 deletion samples/python/queues/sub-norm.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def db_connect() -> oracledb.Connection:
"auth_type": config.oci_auth_type,
"scope": config.db_token_scope,
}
if config.oci_auth_type == "ConfigFileAuthentication":
if config.oci_auth_type in ["ConfigFileAuthentication", "SecurityToken"]:
token_based_auth["profile"] = config.oci_profile

extra_connect_params = {}
Expand Down
2 changes: 1 addition & 1 deletion samples/python/queues/sub-raw.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def db_connect() -> oracledb.Connection:
"auth_type": config.oci_auth_type,
"scope": config.db_token_scope,
}
if config.oci_auth_type == "ConfigFileAuthentication":
if config.oci_auth_type in ["ConfigFileAuthentication", "SecurityToken"]:
token_based_auth["profile"] = config.oci_profile

extra_connect_params = {}
Expand Down
2 changes: 1 addition & 1 deletion samples/terraform/iot-from-scratch/locals.tf
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,6 @@ locals {
locals {
prefixed_allow_listed_identity_groups = [
for group_name in var.db_allow_listed_identity_group_names :
"${var.tenancy_id}:${group_name}"
strcontains(group_name, ":") ? group_name : "${var.tenancy_id}:${group_name}"
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,5 @@
# List of (Dynamic) Groups to allowlist for direct database access.
# If the (Dynamic) Group is not in the Default Identity Domain, it must be prefixed
# by the Identity Domain name. Eg.: "IdentityDomainName/IdentityGroupName"
# If the group is not in this tenancy, prefix by "<other tenancy OCID>:"
# db_allow_listed_identity_group_names =[]
3 changes: 2 additions & 1 deletion samples/terraform/iot-from-scratch/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,8 @@ variable "db_allow_listed_identity_group_names" {
description = <<-DESC
List of (Dynamic) Groups to allowlist for direct database access.
If the (Dynamic) Group is not in the Default Identity Domain, it must be prefixed
by the Identity Domain name. Eg.: "IdentityDomainName/IdentityGroupName"
by the Identity Domain name. Eg.: "IdentityDomainName/IdentityGroupName".
If the group is not in this tenancy, prefix by "<other tenancy OCID>:".
DESC
type = list(string)
default = []
Expand Down