Compare commits
13 Commits
7dc7d6abd8
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea013e4eca | ||
|
|
fcc8c811cc | ||
|
|
3dd5974d70 | ||
|
|
007f30a786 | ||
|
|
b8c18689bf | ||
|
|
7314eef7a9 | ||
|
|
abd00f9e04 | ||
|
|
e9715dc239 | ||
|
|
3003ee530f | ||
|
|
3bebce0f4b | ||
|
|
1c0a5e2697 | ||
|
|
520dc2f371 | ||
|
|
e3e3902891 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -18,3 +18,6 @@ Python/dist/*
|
|||||||
*.spec
|
*.spec
|
||||||
dist/*
|
dist/*
|
||||||
*.7z
|
*.7z
|
||||||
|
*.tar
|
||||||
|
*.tar.gz
|
||||||
|
*.zip
|
||||||
@@ -40,7 +40,7 @@ allowed_name_characters.append('.')
|
|||||||
logging.basicConfig(format='%(asctime)s %(levelname)-8s %(message)s', filename='data_config_debug.log', filemode='w', level=logging.DEBUG, datefmt='%Y%m%d%H%M%S')
|
logging.basicConfig(format='%(asctime)s %(levelname)-8s %(message)s', filename='data_config_debug.log', filemode='w', level=logging.DEBUG, datefmt='%Y%m%d%H%M%S')
|
||||||
|
|
||||||
dir_name = 'I-Sight_Generated_Files'
|
dir_name = 'I-Sight_Generated_Files'
|
||||||
input_datamodel = 'DATAMODEL_1.0.6_LIBERTY.xlsx'
|
input_datamodel = 'DATAMODEL_Ruakaka_QWE1.xlsx'
|
||||||
shell_script_name = dir_name + '/I-Sight_Configuration_'
|
shell_script_name = dir_name + '/I-Sight_Configuration_'
|
||||||
global_shell_script_name = dir_name + '/I-Sight_Global_Configuration.sh'
|
global_shell_script_name = dir_name + '/I-Sight_Global_Configuration.sh'
|
||||||
|
|
||||||
@@ -88,7 +88,7 @@ def excel_parser(sheet_det, slave_no, slave_device, allowed_name_characters, dsh
|
|||||||
logging.debug("Starting registration of Modbus commands for " + str(assets_name[slave_no]) + ".")
|
logging.debug("Starting registration of Modbus commands for " + str(assets_name[slave_no]) + ".")
|
||||||
for index, row in filtered_df_prop.iterrows():
|
for index, row in filtered_df_prop.iterrows():
|
||||||
step2_data = {}
|
step2_data = {}
|
||||||
logging.debug("Registering command " + row['metric_name'] + "...")
|
logging.debug("Registering command " + row['metric_name'] + " on address " + str(row['modbus_address']))
|
||||||
if allowed_data_sizes[allowed_data_types.index(row['type'])] != 1 and int(row['modbus_quantity']) % allowed_data_sizes[allowed_data_types.index(row['type'])] != 0:
|
if allowed_data_sizes[allowed_data_types.index(row['type'])] != 1 and int(row['modbus_quantity']) % allowed_data_sizes[allowed_data_types.index(row['type'])] != 0:
|
||||||
logging.debug("Wrong quantity (" + str(int(row['modbus_quantity'])) + ") for data type " + str(row['type']) + ", using default size " + str(allowed_data_sizes[allowed_data_types.index(row['type'])]) + ".")
|
logging.debug("Wrong quantity (" + str(int(row['modbus_quantity'])) + ") for data type " + str(row['type']) + ", using default size " + str(allowed_data_sizes[allowed_data_types.index(row['type'])]) + ".")
|
||||||
step2_data["readQuantity"] = allowed_data_sizes[allowed_data_types.index(row['type'])]
|
step2_data["readQuantity"] = allowed_data_sizes[allowed_data_types.index(row['type'])]
|
||||||
@@ -104,10 +104,11 @@ def excel_parser(sheet_det, slave_no, slave_device, allowed_name_characters, dsh
|
|||||||
else:
|
else:
|
||||||
logging.debug("Poll interval undefined, using 1000ms as default.")
|
logging.debug("Poll interval undefined, using 1000ms as default.")
|
||||||
step2_data["pollInterval"] = 1000
|
step2_data["pollInterval"] = 1000
|
||||||
if row['endian_swap']:
|
if not math.isnan(row['endian_swap']):
|
||||||
|
logging.debug(f"Endian Swap: {row['endian_swap']} {row['metric_name']}")
|
||||||
step2_data["swap"] = int(row['endian_swap'])
|
step2_data["swap"] = int(row['endian_swap'])
|
||||||
else:
|
else:
|
||||||
logging.debug("Endian Swap undefined, not using swap as default.")
|
logging.debug(f"Endian Swap undefined, not using swap as default. {row['metric_name']}")
|
||||||
step2_data["swap"] = 0
|
step2_data["swap"] = 0
|
||||||
step2_data["dataType"] = row['type']
|
step2_data["dataType"] = row['type']
|
||||||
if not math.isnan(row['scaling_factor']):
|
if not math.isnan(row['scaling_factor']):
|
||||||
@@ -207,7 +208,7 @@ def jq_filter(current_device, dsh, dsh_global, row_device):
|
|||||||
jq_data["enable"] = False
|
jq_data["enable"] = False
|
||||||
jq_data["properties"] = [{"key": "deviceType", "value": "AC_GATEWAY"}, {"key": "cdid", "value": current_device}]
|
jq_data["properties"] = [{"key": "deviceType", "value": "AC_GATEWAY"}, {"key": "cdid", "value": current_device}]
|
||||||
jq_data["outputTopic"] = filter
|
jq_data["outputTopic"] = filter
|
||||||
jq_data["sendOutThreshold"] = {"mode": "bySize", "size": int(128000), "time": int(30), "sizeIdleTimer": {"enable": True, "time": int(30)}}
|
jq_data["sendOutThreshold"] = {"mode": "bySize", "size": int(128000), "time": int(30), "sizeIdleTimer": {"enable": True, "time": int(5)}}
|
||||||
jq_data["minPublishInterval"] = int(0)
|
jq_data["minPublishInterval"] = int(0)
|
||||||
jq_data["samplingMode"] = "allValues"
|
jq_data["samplingMode"] = "allValues"
|
||||||
jq_data["customSamplingRate"] = False
|
jq_data["customSamplingRate"] = False
|
||||||
@@ -245,6 +246,80 @@ def jq_filter(current_device, dsh, dsh_global, row_device):
|
|||||||
dsh_global.write("echo -e \"\\n\" >> global_config.log\n\n")
|
dsh_global.write("echo -e \"\\n\" >> global_config.log\n\n")
|
||||||
dsh_global.write("echo \"Finished work on " + str(row_device['device_name']) + "\"")
|
dsh_global.write("echo \"Finished work on " + str(row_device['device_name']) + "\"")
|
||||||
|
|
||||||
|
if filter == "generic_metrics":
|
||||||
|
logging.debug("Creating OnEvent telemetry topic.")
|
||||||
|
jq_data = {}
|
||||||
|
jq_data["enable"] = False
|
||||||
|
jq_data["properties"] = [{"key": "deviceType", "value": "AC_GATEWAY"}, {"key": "cdid", "value": current_device}]
|
||||||
|
jq_data["outputTopic"] = "PCS_OnEvent"
|
||||||
|
jq_data["sendOutThreshold"] = {"mode": "immediately", "size": int(4096), "time": int(60), "sizeIdleTimer": {"enable": True, "time": int(60)}}
|
||||||
|
jq_data["minPublishInterval"] = int(0)
|
||||||
|
jq_data["samplingMode"] = "allChangedValues"
|
||||||
|
jq_data["customSamplingRate"] = False
|
||||||
|
jq_data["pollingInterval"] = int(0)
|
||||||
|
jq_data["onChange"] = True
|
||||||
|
final_filter = row["jq_filter"].replace("***device_name***", current_device)
|
||||||
|
cmd_list = {}
|
||||||
|
jq_data["format"] = final_filter
|
||||||
|
jq_data["tags"]= {"modbus_tcp_master": cmd_list}
|
||||||
|
json_object = json.dumps(jq_data)
|
||||||
|
dsh.write("# [STEP 4] Creating " + filter + " OnEvent Azure telemetry topic\n\n")
|
||||||
|
dsh.write(
|
||||||
|
inspect.cleandoc(
|
||||||
|
"""sudo curl -X POST https://127.0.0.1:8443/api/v1/azure-iotedge/messages \\
|
||||||
|
-H "Content-Type: application/json" \\
|
||||||
|
-H "mx-api-token:$(sudo cat /var/thingspro/data/mx-api-token)" \\
|
||||||
|
-d """) + "'" + str(json_object) +"'" + """ -k | jq
|
||||||
|
\n"""
|
||||||
|
)
|
||||||
|
dsh.write('printf "\\n \\n" >> data_shell_script.log\n')
|
||||||
|
dsh.write("\n\n")
|
||||||
|
|
||||||
|
dsh_global.write("### Creating OnEvent Azure messages " + "" + " for " + str(row_device['device_name']) + "\n")
|
||||||
|
dsh_global.write("curl -s -X POST -k https://" + row_device['device_ip_address_http'] + ":8443/api/v1/azure-iotedge/messages \\\n")
|
||||||
|
dsh_global.write("\t-H \"Content-Type: application/json\" \\\n")
|
||||||
|
dsh_global.write("\t-H \"mx-api-token: ${token}\" \\\n")
|
||||||
|
dsh_global.write("\t-d '" + str(json_object) + "' >> global_config.log\n\n")
|
||||||
|
dsh_global.write("echo -e \"\\n\" >> global_config.log\n\n")
|
||||||
|
dsh_global.write("echo \"Finished work on " + str(row_device['device_name']) + "\"")
|
||||||
|
|
||||||
|
logging.debug("Creating 12H telemetry topic.")
|
||||||
|
jq_data = {}
|
||||||
|
jq_data["enable"] = False
|
||||||
|
jq_data["properties"] = [{"key": "deviceType", "value": "AC_GATEWAY"}, {"key": "cdid", "value": current_device}]
|
||||||
|
jq_data["outputTopic"] = "PCS_12H"
|
||||||
|
jq_data["sendOutThreshold"] = {"mode": "byTime", "size": int(4096), "time": int(43200), "sizeIdleTimer": {"enable": True, "time": int(60)}}
|
||||||
|
jq_data["minPublishInterval"] = int(0)
|
||||||
|
jq_data["samplingMode"] = "latestValues"
|
||||||
|
jq_data["customSamplingRate"] = False
|
||||||
|
jq_data["pollingInterval"] = int(43200)
|
||||||
|
jq_data["onChange"] = False
|
||||||
|
final_filter = row["jq_filter"].replace("***device_name***", current_device)
|
||||||
|
cmd_list = {}
|
||||||
|
jq_data["format"] = final_filter
|
||||||
|
jq_data["tags"]= {"modbus_tcp_master": cmd_list}
|
||||||
|
json_object = json.dumps(jq_data)
|
||||||
|
dsh.write("# [STEP 4] Creating " + filter + " 12H Azure telemetry topic\n\n")
|
||||||
|
dsh.write(
|
||||||
|
inspect.cleandoc(
|
||||||
|
"""sudo curl -X POST https://127.0.0.1:8443/api/v1/azure-iotedge/messages \\
|
||||||
|
-H "Content-Type: application/json" \\
|
||||||
|
-H "mx-api-token:$(sudo cat /var/thingspro/data/mx-api-token)" \\
|
||||||
|
-d """) + "'" + str(json_object) +"'" + """ -k | jq
|
||||||
|
\n"""
|
||||||
|
)
|
||||||
|
dsh.write('printf "\\n \\n" >> data_shell_script.log\n')
|
||||||
|
dsh.write("\n\n")
|
||||||
|
|
||||||
|
dsh_global.write("### Creating 12H Azure messages " + "" + " for " + str(row_device['device_name']) + "\n")
|
||||||
|
dsh_global.write("curl -s -X POST -k https://" + row_device['device_ip_address_http'] + ":8443/api/v1/azure-iotedge/messages \\\n")
|
||||||
|
dsh_global.write("\t-H \"Content-Type: application/json\" \\\n")
|
||||||
|
dsh_global.write("\t-H \"mx-api-token: ${token}\" \\\n")
|
||||||
|
dsh_global.write("\t-d '" + str(json_object) + "' >> global_config.log\n\n")
|
||||||
|
dsh_global.write("echo -e \"\\n\" >> global_config.log\n\n")
|
||||||
|
dsh_global.write("echo \"Finished work on " + str(row_device['device_name']) + "\"")
|
||||||
|
|
||||||
|
|
||||||
STATIC_JQ_FILTER_EMISSIONDATE = "(now|todateiso8601)"
|
STATIC_JQ_FILTER_EMISSIONDATE = "(now|todateiso8601)"
|
||||||
|
|
||||||
def bitfield_jq_filter(current_device):
|
def bitfield_jq_filter(current_device):
|
||||||
|
|||||||
@@ -1,27 +1,35 @@
|
|||||||
from azure.iot.hub import IoTHubRegistryManager
|
from azure.iot.hub import IoTHubRegistryManager
|
||||||
from azure.iot.hub.models import Twin, TwinProperties
|
from azure.iot.hub.models import Twin, TwinProperties
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
import pandas as pd
|
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
import os
|
import os
|
||||||
import json
|
|
||||||
|
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
INPUT_FILE = "cottonwood_devices.xlsx" # Path to your Excel file
|
INPUT_FILE = "HONEYCOMB_DEVICE_LIST.xlsx" # Path to your Excel file
|
||||||
SITE_NAME = "COTTONWOOD" # Parameterized site name
|
SITE_NAME = "Honeycomb" # Parameterized site name
|
||||||
VERSION = "1.5.0" # Parameterized version
|
VERSION = "1.6.0" # Parameterized version
|
||||||
|
|
||||||
# Authenticate to your Azure account
|
# Authenticate to your Azure account
|
||||||
# CONNECTION_STRING = str(os.getenv("CONNECTION_STRING_SAFT_PROD"))
|
|
||||||
CONNECTION_STRING = str(os.getenv("CONNECTION_STRING_INOX_PROD"))
|
CONNECTION_STRING = str(os.getenv("CONNECTION_STRING_INOX_PROD"))
|
||||||
if CONNECTION_STRING == "":
|
if CONNECTION_STRING == "" or CONNECTION_STRING == "None":
|
||||||
print("Provide a connection string for the Iot Hub before running the script!")
|
print("Provide a connection string for the Iot Hub before running the script!")
|
||||||
exit(13)
|
exit(13)
|
||||||
|
|
||||||
|
# Extract HostName from CONNECTION_STRING
|
||||||
|
host_name = ""
|
||||||
|
for part in CONNECTION_STRING.split(';'):
|
||||||
|
if part.startswith('HostName='):
|
||||||
|
host_name = part.split('=', 1)[1]
|
||||||
|
break
|
||||||
|
|
||||||
df = pd.read_excel(INPUT_FILE, header=None)
|
df = pd.read_excel(INPUT_FILE, header=None)
|
||||||
|
|
||||||
|
# Ensure the dataframe has at least 2 columns to store the connection strings
|
||||||
|
if df.shape[1] < 2:
|
||||||
|
df[1] = ""
|
||||||
|
|
||||||
registry_manager = IoTHubRegistryManager.from_connection_string(CONNECTION_STRING)
|
registry_manager = IoTHubRegistryManager.from_connection_string(CONNECTION_STRING)
|
||||||
|
|
||||||
def create_device(device_name, number):
|
def create_device(device_name, number):
|
||||||
@@ -34,6 +42,18 @@ def create_device(device_name, number):
|
|||||||
iot_edge=True
|
iot_edge=True
|
||||||
)
|
)
|
||||||
print(f"Created IoT Edge-enabled device: {device_name}")
|
print(f"Created IoT Edge-enabled device: {device_name}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error creating {device_name} (it may already exist): {e}")
|
||||||
|
try:
|
||||||
|
device = registry_manager.get_device(device_name)
|
||||||
|
print(f"Retrieved existing device: {device_name}")
|
||||||
|
except Exception as e2:
|
||||||
|
print(f"Error retrieving {device_name}: {e2}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
primary_key = device.authentication.symmetric_key.primary_key
|
||||||
|
connection_string = f"HostName={host_name};DeviceId={device_name};SharedAccessKey={primary_key}"
|
||||||
|
|
||||||
# Set tags
|
# Set tags
|
||||||
twin = registry_manager.get_twin(device_name)
|
twin = registry_manager.get_twin(device_name)
|
||||||
@@ -44,13 +64,24 @@ def create_device(device_name, number):
|
|||||||
})
|
})
|
||||||
registry_manager.update_twin(device_name, twin_patch, twin.etag)
|
registry_manager.update_twin(device_name, twin_patch, twin.etag)
|
||||||
print(f"Updated tags for {device_name}")
|
print(f"Updated tags for {device_name}")
|
||||||
|
|
||||||
|
return connection_string
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error processing {device_name}: {e}")
|
print(f"Error processing {device_name}: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
# Loop through the Excel file and process each device
|
# Loop through the Excel file and process each device
|
||||||
for index, row in df.iterrows():
|
for index, row in df.iterrows():
|
||||||
device_name = str(row[0]).strip()
|
device_name = str(row[0]).strip()
|
||||||
if device_name:
|
if device_name and device_name != 'nan':
|
||||||
create_device(device_name, index + 1)
|
conn_str = create_device(device_name, index + 1)
|
||||||
|
if conn_str:
|
||||||
|
df.at[index, 1] = conn_str
|
||||||
|
|
||||||
print("Device provisioning completed.")
|
# Save the updated dataframe back to the Excel file
|
||||||
|
try:
|
||||||
|
df.to_excel(INPUT_FILE, index=False, header=False)
|
||||||
|
print("Device provisioning completed.")
|
||||||
|
except PermissionError:
|
||||||
|
print(f"\n[ERROR] Permission denied: '{INPUT_FILE}'.")
|
||||||
|
print("Please close the Excel file if it's currently open in another program, and then try again.")
|
||||||
|
|||||||
@@ -15,20 +15,21 @@ if CONNECTION_STRING == "":
|
|||||||
print("Provide a connection string for the Iot Hub before running the script!")
|
print("Provide a connection string for the Iot Hub before running the script!")
|
||||||
exit(13)
|
exit(13)
|
||||||
|
|
||||||
SITE_NAME = "LIBERTY"
|
SITE_NAME = "Bell"
|
||||||
|
|
||||||
|
|
||||||
registry_manager = IoTHubRegistryManager.from_connection_string(CONNECTION_STRING)
|
registry_manager = IoTHubRegistryManager.from_connection_string(CONNECTION_STRING)
|
||||||
query_spec = QuerySpecification(query="SELECT * FROM devices WHERE IS_DEFINED(tags.site) AND tags.site = '" + SITE_NAME + "' AND capabilities.iotEdge = true")
|
#query_spec = QuerySpecification(query="SELECT * FROM devices WHERE IS_DEFINED(tags.site) AND tags.site = '" + SITE_NAME + "' AND capabilities.iotEdge = true")
|
||||||
|
query_spec = QuerySpecification(query="SELECT * FROM devices WHERE IS_DEFINED(tags.site) AND tags.site = '" + SITE_NAME + "' AND capabilities.iotEdge = false")
|
||||||
|
|
||||||
query_result = registry_manager.query_iot_hub(query_spec)
|
query_result = registry_manager.query_iot_hub(query_spec)
|
||||||
|
|
||||||
devices = []
|
devices = []
|
||||||
for item in query_result.items:
|
for item in query_result.items:
|
||||||
deviceId = str(item.device_id)
|
deviceId = str(item.device_id)
|
||||||
site = str(item.tags['site'])
|
site = str(item.tags.get('site')) if item.tags.get('site') else None
|
||||||
number = int(item.tags['number'])
|
number = int(item.tags.get('number')) if item.tags.get('number') else None
|
||||||
cloud_version = str(item.tags['version'])
|
cloud_version = str(item.tags.get('version')) if item.tags.get('version') else None
|
||||||
devices.append([deviceId, site, number, cloud_version])
|
devices.append([deviceId, site, number, cloud_version])
|
||||||
|
|
||||||
ordered_devices = sorted(devices, key = lambda x: (x[1], x[2]))
|
ordered_devices = sorted(devices, key = lambda x: (x[1], x[2]))
|
||||||
|
|||||||
@@ -30,15 +30,20 @@ for twin in query_result.items:
|
|||||||
"device_id": twin.device_id,
|
"device_id": twin.device_id,
|
||||||
"number": twin.tags.get("number") if twin.tags else None,
|
"number": twin.tags.get("number") if twin.tags else None,
|
||||||
"site": twin.tags.get("site") if twin.tags else None,
|
"site": twin.tags.get("site") if twin.tags else None,
|
||||||
|
"subsite": twin.tags.get("subsite") if twin.tags else None,
|
||||||
"connection_state": twin.connection_state,
|
"connection_state": twin.connection_state,
|
||||||
"last_activity_time": twin.last_activity_time,
|
"last_activity_time": twin.last_activity_time
|
||||||
})
|
})
|
||||||
|
|
||||||
df = pd.DataFrame(rows)
|
df = pd.DataFrame(rows)
|
||||||
|
df['number'] = pd.to_numeric(df['number'], errors='coerce')
|
||||||
|
df['number'] = df['number'].astype('Int64')
|
||||||
df_sorted = df.sort_values(by=["site", "number"]).reset_index(drop=True)
|
df_sorted = df.sort_values(by=["site", "number"]).reset_index(drop=True)
|
||||||
|
|
||||||
print(df_sorted)
|
for row in df_sorted.itertuples():
|
||||||
|
if "cube" not in row.device_id:
|
||||||
|
print(f"\"{row.device_id}\", \"{row.site}\", \"{row.number}\",")
|
||||||
|
|
||||||
|
|
||||||
# Compute difference in hours (float)
|
# Compute difference in hours (float)
|
||||||
df_sorted["time_since_last_activity_hours"] = df_sorted["last_activity_time"].apply(
|
df_sorted["time_since_last_activity_hours"] = df_sorted["last_activity_time"].apply(
|
||||||
@@ -56,4 +61,4 @@ if "last_activity_time" in df_sorted.columns:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
df_sorted.to_excel("iot_devices.xlsx", index=False)
|
df_sorted.to_excel("iot_devices_2.xlsx", index=False)
|
||||||
|
|||||||
64
Python/azure_iot_hub_thingspro_api_call.py
Normal file
64
Python/azure_iot_hub_thingspro_api_call.py
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
from azure.iot.hub import IoTHubRegistryManager
|
||||||
|
from azure.iot.hub.protocol.models import QuerySpecification, Module
|
||||||
|
from azure.iot.hub.models import CloudToDeviceMethod, CloudToDeviceMethodResult
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from isight_device import iSightDevice
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
CONNECTION_STRING = str(os.getenv("CONNECTION_STRING_INOX_PROD"))
|
||||||
|
if CONNECTION_STRING == "":
|
||||||
|
print("Provide a connection string for the Iot Hub before running the script!")
|
||||||
|
exit(13)
|
||||||
|
|
||||||
|
registry_manager = IoTHubRegistryManager.from_connection_string(CONNECTION_STRING)
|
||||||
|
query_spec = QuerySpecification(query="SELECT * FROM devices WHERE IS_DEFINED(tags.site) AND capabilities.iotEdge = true ")
|
||||||
|
|
||||||
|
query_result = registry_manager.query_iot_hub(query_spec)
|
||||||
|
|
||||||
|
devices = []
|
||||||
|
for item in query_result.items:
|
||||||
|
devices.append(iSightDevice(str(item.device_id), str(item.tags['site']), int(item.tags['number']), str(item.tags['version'])))
|
||||||
|
|
||||||
|
devices.sort(key = lambda d: (d.site, d.number))
|
||||||
|
|
||||||
|
rows = []
|
||||||
|
|
||||||
|
for device in devices:
|
||||||
|
print(device, end="\t")
|
||||||
|
current_device_modules = registry_manager.get_modules(device.deviceId)
|
||||||
|
for module in current_device_modules:
|
||||||
|
if (module.module_id == "thingspro-agent"):
|
||||||
|
device.setModule(module)
|
||||||
|
method_name = "thingspro-api-v1"
|
||||||
|
payload = {
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/device/general"
|
||||||
|
}
|
||||||
|
module_id = "thingspro-agent"
|
||||||
|
try:
|
||||||
|
direct_method = CloudToDeviceMethod(method_name = method_name, payload = payload)
|
||||||
|
response = registry_manager.invoke_device_module_method(device_id = device.deviceId, module_id = module_id, direct_method_request = direct_method)
|
||||||
|
#print(response)
|
||||||
|
#print(json.dumps(response.payload, indent = 2))
|
||||||
|
hostname = response.payload['data']['hostName']
|
||||||
|
serial = response.payload['data']['serialNumber']
|
||||||
|
version = response.payload['data']['firmwareVersion']
|
||||||
|
description = response.payload['data']['description']
|
||||||
|
print(hostname + " " + serial + " " + version + " " + description)
|
||||||
|
rows.append({
|
||||||
|
"hostname": hostname,
|
||||||
|
"site": device.getSite(),
|
||||||
|
"number": device.getNumber(),
|
||||||
|
"cloud_version": device.getVersion(),
|
||||||
|
"device_version": version
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error: {e}")
|
||||||
|
|
||||||
|
df = pd.DataFrame(rows)
|
||||||
|
df.to_excel("thingspro-version.xlsx", index=False)
|
||||||
55
Python/azure_iot_hub_thingspro_api_call_generic.py
Normal file
55
Python/azure_iot_hub_thingspro_api_call_generic.py
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
from azure.iot.hub import IoTHubRegistryManager
|
||||||
|
from azure.iot.hub.protocol.models import QuerySpecification, Module
|
||||||
|
from azure.iot.hub.models import CloudToDeviceMethod, CloudToDeviceMethodResult
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from isight_device import iSightDevice
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
CONNECTION_STRING = str(os.getenv("CONNECTION_STRING_INOX_PROD"))
|
||||||
|
if CONNECTION_STRING == "":
|
||||||
|
print("Provide a connection string for the Iot Hub before running the script!")
|
||||||
|
exit(13)
|
||||||
|
|
||||||
|
registry_manager = IoTHubRegistryManager.from_connection_string(CONNECTION_STRING)
|
||||||
|
query_spec = QuerySpecification(query="SELECT * FROM devices WHERE IS_DEFINED(tags.site) AND capabilities.iotEdge = true ")
|
||||||
|
|
||||||
|
query_result = registry_manager.query_iot_hub(query_spec)
|
||||||
|
|
||||||
|
devices = []
|
||||||
|
for item in query_result.items:
|
||||||
|
devices.append(iSightDevice(str(item.device_id), str(item.tags['site']), int(item.tags['number']), str(item.tags['version'])))
|
||||||
|
|
||||||
|
devices.sort(key = lambda d: (d.site, d.number))
|
||||||
|
|
||||||
|
rows = []
|
||||||
|
|
||||||
|
for device in devices:
|
||||||
|
if device.getDeviceId() == "TBAIB1114211":
|
||||||
|
print(device, end="\t")
|
||||||
|
current_device_modules = registry_manager.get_modules(device.deviceId)
|
||||||
|
for module in current_device_modules:
|
||||||
|
if (module.module_id == "thingspro-agent"):
|
||||||
|
device.setModule(module)
|
||||||
|
method_name = "thingspro-api-v1"
|
||||||
|
payload = {
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/device/time"
|
||||||
|
}
|
||||||
|
module_id = "thingspro-agent"
|
||||||
|
try:
|
||||||
|
direct_method = CloudToDeviceMethod(method_name = method_name, payload = payload)
|
||||||
|
response = registry_manager.invoke_device_module_method(device_id = device.deviceId, module_id = module_id, direct_method_request = direct_method)
|
||||||
|
print(json.dumps(response.payload, indent = 2))
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error: {e}")
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
print(f"{device.getDeviceId()}")
|
||||||
|
|
||||||
|
df = pd.DataFrame(rows)
|
||||||
|
df.to_excel("thingspro-version.xlsx", index=False)
|
||||||
69
Python/azure_iot_hub_thingspro_api_call_user.py
Normal file
69
Python/azure_iot_hub_thingspro_api_call_user.py
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
from azure.iot.hub import IoTHubRegistryManager
|
||||||
|
from azure.iot.hub.protocol.models import QuerySpecification, Module
|
||||||
|
from azure.iot.hub.models import CloudToDeviceMethod, CloudToDeviceMethodResult
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from isight_device import iSightDevice
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
CONNECTION_STRING = str(os.getenv("CONNECTION_STRING_INOX_PROD"))
|
||||||
|
NEW_MOXA_PASSWORD = str(os.getenv("NEW_MOXA_PASSWORD"))
|
||||||
|
if CONNECTION_STRING == "":
|
||||||
|
print("Provide a connection string for the Iot Hub before running the script!")
|
||||||
|
exit(13)
|
||||||
|
|
||||||
|
registry_manager = IoTHubRegistryManager.from_connection_string(CONNECTION_STRING)
|
||||||
|
query_spec = QuerySpecification(query="SELECT * FROM devices WHERE IS_DEFINED(tags.site) AND capabilities.iotEdge = true ")
|
||||||
|
|
||||||
|
query_result = registry_manager.query_iot_hub(query_spec)
|
||||||
|
|
||||||
|
devices = []
|
||||||
|
for item in query_result.items:
|
||||||
|
devices.append(iSightDevice(str(item.device_id), str(item.tags['site']), int(item.tags['number']), str(item.tags['version'])))
|
||||||
|
|
||||||
|
devices.sort(key = lambda d: (d.site, d.number))
|
||||||
|
|
||||||
|
rows = []
|
||||||
|
|
||||||
|
for device in devices:
|
||||||
|
current_device_modules = registry_manager.get_modules(device.deviceId)
|
||||||
|
for module in current_device_modules:
|
||||||
|
if (module.module_id == "thingspro-agent"):
|
||||||
|
device.setModule(module)
|
||||||
|
method_name = "thingspro-api-v1"
|
||||||
|
payload = {
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/users"
|
||||||
|
}
|
||||||
|
module_id = "thingspro-agent"
|
||||||
|
try:
|
||||||
|
direct_method = CloudToDeviceMethod(method_name = method_name, payload = payload)
|
||||||
|
response = registry_manager.invoke_device_module_method(device_id = device.deviceId, module_id = module_id, direct_method_request = direct_method)
|
||||||
|
for i in range(int(response.payload['count'])):
|
||||||
|
username = response.payload['data'][i]["name"]
|
||||||
|
userid = int(response.payload['data'][i]["id"])
|
||||||
|
if username == "admin":
|
||||||
|
print(f"Found {username} at ID {str(userid)} on device {device.getDeviceId()} from {device.getSite()} ({device.getNumber()})")
|
||||||
|
try:
|
||||||
|
payloadpassword = {
|
||||||
|
"method": "PUT",
|
||||||
|
"path": f"/users/{str(userid)}/password",
|
||||||
|
"requestBody": {
|
||||||
|
"newPassword": f"{NEW_MOXA_PASSWORD}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
direct_method_password = CloudToDeviceMethod(method_name = method_name, payload = payloadpassword)
|
||||||
|
response_password = registry_manager.invoke_device_module_method(device_id = device.deviceId, module_id = module_id, direct_method_request = direct_method_password)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error while changing password on device {device.getDeviceId()} from {device.getSite()} ({device.getNumber()})")
|
||||||
|
continue
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error while getting users on device {device.getDeviceId()} from {device.getSite()} ({device.getNumber()})")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# df = pd.DataFrame(rows)
|
||||||
|
# df.to_excel("thingspro-users.xlsx", index=False)
|
||||||
@@ -87,7 +87,7 @@ def set_ssh_status(base_url, token):
|
|||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def activate_ssh(ip_address):
|
def activate_ssh(ip_address, silent = False):
|
||||||
|
|
||||||
# Ensure the URL uses HTTPS
|
# Ensure the URL uses HTTPS
|
||||||
url = ip_address
|
url = ip_address
|
||||||
@@ -104,17 +104,23 @@ def activate_ssh(ip_address):
|
|||||||
if not verify_ssl:
|
if not verify_ssl:
|
||||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||||
|
|
||||||
print(f"HTTPS", end=" ", flush=True)
|
if not silent:
|
||||||
|
print(f"HTTPS", end=" ", flush=True)
|
||||||
try:
|
try:
|
||||||
token = authenticate(url)
|
token = authenticate(url)
|
||||||
print(f"✅", end="", flush=True)
|
if not silent:
|
||||||
|
print(f"✅", end="", flush=True)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"❌", flush=True)
|
if not silent:
|
||||||
|
print(f"❌", flush=True)
|
||||||
raise
|
raise
|
||||||
print(f"SSH", end=" ", flush=True)
|
if not silent:
|
||||||
|
print(f"SSH", end=" ", flush=True)
|
||||||
try:
|
try:
|
||||||
set_ssh_status(url, token)
|
set_ssh_status(url, token)
|
||||||
print(f"✅", end="\n", flush=True)
|
if not silent:
|
||||||
|
print(f"✅", end="\n", flush=True)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"❌", flush=True)
|
if not silent:
|
||||||
|
print(f"❌", flush=True)
|
||||||
raise
|
raise
|
||||||
118
Python/cube_activate_ssh_port.py
Normal file
118
Python/cube_activate_ssh_port.py
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
import requests
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import urllib3
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
import time
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def resource_path(relative_path):
|
||||||
|
""" Get absolute path to resource, works for dev and for PyInstaller """
|
||||||
|
try:
|
||||||
|
# PyInstaller creates a temp folder and stores path in _MEIPASS
|
||||||
|
base_path = sys._MEIPASS
|
||||||
|
except Exception:
|
||||||
|
base_path = os.path.abspath(".")
|
||||||
|
|
||||||
|
return os.path.join(base_path, relative_path)
|
||||||
|
|
||||||
|
dotenv_path = resource_path('.env')
|
||||||
|
load_dotenv(dotenv_path=dotenv_path)
|
||||||
|
|
||||||
|
def authenticate(base_url):
|
||||||
|
"""
|
||||||
|
Authenticate with the CUBE API using username, password and certificate.
|
||||||
|
Returns the JWT token if successful.
|
||||||
|
"""
|
||||||
|
auth_url = f"{base_url}/api/auth"
|
||||||
|
|
||||||
|
ENV_WEB = {
|
||||||
|
"DEFAULT_CUBE_WEB_ADMIN_USER": os.getenv("DEFAULT_CUBE_WEB_ADMIN_USER"),
|
||||||
|
"DEFAULT_CUBE_WEB_ADMIN_PASSWORD": os.getenv("DEFAULT_CUBE_WEB_ADMIN_PASSWORD"),
|
||||||
|
"DEFAULT_CERTIFICATE": os.getenv("DEFAULT_CERTIFICATE")
|
||||||
|
}
|
||||||
|
|
||||||
|
username = ENV_WEB["DEFAULT_CUBE_WEB_ADMIN_USER"]
|
||||||
|
password = ENV_WEB["DEFAULT_CUBE_WEB_ADMIN_PASSWORD"]
|
||||||
|
certificate = ENV_WEB["DEFAULT_CERTIFICATE"].encode("utf-8")
|
||||||
|
|
||||||
|
auth_params = {
|
||||||
|
"login": username,
|
||||||
|
"password": password
|
||||||
|
}
|
||||||
|
files = {
|
||||||
|
"params": (None, json.dumps(auth_params), "application/json"),
|
||||||
|
"certificate": ("certificate.pem", certificate, "application/octet-stream")
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
response = requests.post(auth_url, files=files, verify=False, timeout=10)
|
||||||
|
response.raise_for_status() # Raise exception for 4XX/5XX responses
|
||||||
|
|
||||||
|
# Extract token from response
|
||||||
|
auth_data = response.json()
|
||||||
|
token = auth_data.get("token")
|
||||||
|
|
||||||
|
if not token:
|
||||||
|
raise requests.exceptions.RequestException
|
||||||
|
|
||||||
|
print("HTTPS ✅", end = " ", flush=True)
|
||||||
|
return token
|
||||||
|
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
print(f"HTTPS ❌", flush=True)
|
||||||
|
if hasattr(e, 'response') and e.response:
|
||||||
|
raise Exception(e.response)
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
def set_ssh_status(base_url, token):
|
||||||
|
"""
|
||||||
|
Set SSH status (enable) using the provided JWT token.
|
||||||
|
"""
|
||||||
|
ssh_url = f"{base_url}/api/ssh"
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Authorization": f"Bearer {token}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Set new SSH status
|
||||||
|
payload = { "currentStatus": True }
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.post(ssh_url, headers=headers, json=payload, verify=False, timeout=10)
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
print(f"SSH ✅", end = " ", flush=True)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
print("SSH ❌", flush=True)
|
||||||
|
if hasattr(e, 'response') and e.response:
|
||||||
|
raise Exception(e.response)
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
def activate_ssh(ip_address, port):
|
||||||
|
|
||||||
|
# Ensure the URL uses HTTPS
|
||||||
|
url = ip_address
|
||||||
|
if not url.startswith("https://"):
|
||||||
|
# Convert http:// to https:// or add https:// if no protocol specified
|
||||||
|
if url.startswith("http://"):
|
||||||
|
url = "https://" + url[7:]
|
||||||
|
else:
|
||||||
|
url = "https://" + url
|
||||||
|
if not url.endswith(f":{port}"):
|
||||||
|
url = url + f":{port}"
|
||||||
|
|
||||||
|
verify_ssl = False
|
||||||
|
if not verify_ssl:
|
||||||
|
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||||
|
|
||||||
|
token = authenticate(url)
|
||||||
|
if not token:
|
||||||
|
return
|
||||||
|
time.sleep(3)
|
||||||
|
set_ssh_status(url, token)
|
||||||
@@ -10,11 +10,11 @@ from azure.iot.hub.models import Twin, TwinProperties
|
|||||||
|
|
||||||
load_dotenv(override=True)
|
load_dotenv(override=True)
|
||||||
|
|
||||||
ip_address_prefix = "10.81.60."
|
ip_address_prefix = "10.81.56."
|
||||||
ssh_command = "hostname"
|
ssh_command = "hostname"
|
||||||
|
|
||||||
csv_filename = "Grandpuits_01.csv"
|
csv_filename = "DK2_01.csv"
|
||||||
SITE_NAME = "Grandpuits"
|
SITE_NAME = "DK2"
|
||||||
|
|
||||||
ssh_username = os.getenv("DEFAULT_CUBE_LINUX_ADMIN_USER")
|
ssh_username = os.getenv("DEFAULT_CUBE_LINUX_ADMIN_USER")
|
||||||
ssh_password = os.getenv("DEFAULT_CUBE_LINUX_ADMIN_PASSWORD")
|
ssh_password = os.getenv("DEFAULT_CUBE_LINUX_ADMIN_PASSWORD")
|
||||||
|
|||||||
141
Python/cube_ssh_batch_passive_port.py
Normal file
141
Python/cube_ssh_batch_passive_port.py
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
import csv
|
||||||
|
import paramiko
|
||||||
|
import time
|
||||||
|
from cube_activate_ssh_port import activate_ssh
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
from azure.iot.hub import IoTHubRegistryManager
|
||||||
|
from azure.iot.hub.models import Twin, TwinProperties
|
||||||
|
|
||||||
|
load_dotenv(override=True)
|
||||||
|
|
||||||
|
ssh_username = os.getenv("DEFAULT_CUBE_LINUX_ADMIN_USER")
|
||||||
|
ssh_password = os.getenv("DEFAULT_CUBE_LINUX_ADMIN_PASSWORD")
|
||||||
|
CONNECTION_STRING = str(os.getenv("CONNECTION_STRING_INOX_PROD"))
|
||||||
|
|
||||||
|
def execute_ssh_command(ip, port, command):
|
||||||
|
client = paramiko.SSHClient()
|
||||||
|
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
|
try:
|
||||||
|
client.connect(ip, port=port, username=ssh_username, password=ssh_password, allow_agent=False, look_for_keys=False)
|
||||||
|
stdin, stdout, stderr = client.exec_command(command)
|
||||||
|
result = stdout.read().decode().lower().strip()
|
||||||
|
return result
|
||||||
|
except Exception as e:
|
||||||
|
print(f"SSH Error: {str(e)}")
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
client.close()
|
||||||
|
|
||||||
|
def update_cloud_config(ip, new_content):
|
||||||
|
client = paramiko.SSHClient()
|
||||||
|
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
|
try:
|
||||||
|
client.connect(ip, port=11022, username=ssh_username, password=ssh_password, allow_agent=False, look_for_keys=False)
|
||||||
|
stdin, stdout, stderr = client.exec_command(f'sudo -S bash -c \'cat > /etc/cube/config-azure.properties << EOF\n{new_content}\nEOF\'\n')
|
||||||
|
stdin.write(ssh_password + "\n")
|
||||||
|
stdin.flush()
|
||||||
|
stdoutput = [line for line in stdout]
|
||||||
|
stderroutput = [line for line in stderr]
|
||||||
|
for output in stdoutput:
|
||||||
|
print(output.strip())
|
||||||
|
except Exception as e:
|
||||||
|
print(f"SSH Error: {str(e)}")
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
client.close()
|
||||||
|
|
||||||
|
def main():
|
||||||
|
|
||||||
|
SITE_NAME = "LIBERTY"
|
||||||
|
|
||||||
|
ip_address = "192.168.15.158"
|
||||||
|
|
||||||
|
https_start_port = 8080
|
||||||
|
https_end_port = 8096
|
||||||
|
https_port_range = list(range(https_start_port, https_end_port + 1))
|
||||||
|
print(f"{https_port_range}")
|
||||||
|
|
||||||
|
ssh_start_port = 8180
|
||||||
|
ssh_start_end = 8196
|
||||||
|
ssh_port_range = list(range(ssh_start_port, ssh_start_end + 1))
|
||||||
|
print(f"{ssh_port_range}")
|
||||||
|
|
||||||
|
|
||||||
|
print(f"Site: {SITE_NAME}")
|
||||||
|
print(f"From {ip_address}:{str(https_start_port)} to {ip_address}:{str(https_end_port)}")
|
||||||
|
|
||||||
|
file_numbering = 0
|
||||||
|
|
||||||
|
while os.path.exists(SITE_NAME + "_" + str(file_numbering) + ".csv"):
|
||||||
|
file_numbering = file_numbering + 1
|
||||||
|
|
||||||
|
csv_filename = SITE_NAME + "_" + str(file_numbering) + ".csv"
|
||||||
|
|
||||||
|
print(f"Logging results to {csv_filename}")
|
||||||
|
|
||||||
|
with open(csv_filename, mode="w", newline="") as file:
|
||||||
|
writer = csv.writer(file)
|
||||||
|
writer.writerow(["Number", "IP address", "Cube ID", "Environment", "Correct configuration"])
|
||||||
|
|
||||||
|
registry_manager = IoTHubRegistryManager.from_connection_string(CONNECTION_STRING)
|
||||||
|
|
||||||
|
results = []
|
||||||
|
|
||||||
|
for i in [0]: # range(0, len(https_port_range)):
|
||||||
|
ip_address = f"{ip_address}"
|
||||||
|
print(f"Activating SSH for {ip_address}:{https_port_range[i]} ", end=" ")
|
||||||
|
|
||||||
|
try:
|
||||||
|
activate_ssh(ip_address, https_port_range[i])
|
||||||
|
except Exception as e:
|
||||||
|
writer.writerow([i, f"{ip_address}:{https_port_range[i]}", "UNREACHABLE", "NA", "NA"])
|
||||||
|
file.flush()
|
||||||
|
continue
|
||||||
|
|
||||||
|
ssh_command = "hostname"
|
||||||
|
print(f"Executing {ssh_command} for {ip_address}:{ssh_port_range[i]}:", end=" ")
|
||||||
|
try:
|
||||||
|
cube_id = execute_ssh_command(ip_address, ssh_port_range[i], ssh_command)
|
||||||
|
except Exception as e:
|
||||||
|
print("Failed!")
|
||||||
|
writer.writerow([i, ip_address, "UNREACHABLE", "NA", "NA"])
|
||||||
|
file.flush()
|
||||||
|
continue
|
||||||
|
print(cube_id)
|
||||||
|
|
||||||
|
ssh_command = "grep \"connection-string\" /etc/cube/config-azure.properties"
|
||||||
|
print(f"Getting configured Connection String for {ip_address}:", end=" ")
|
||||||
|
try:
|
||||||
|
connection_string = execute_ssh_command(ip_address, ssh_port_range[i], ssh_command)
|
||||||
|
if connection_string == "":
|
||||||
|
raise Exception("No Connection String was extracted!")
|
||||||
|
|
||||||
|
iothub_match = re.search(r"hostname\\=(.*?);", connection_string, re.IGNORECASE)
|
||||||
|
iothub = iothub_match.group(1) if iothub_match else None
|
||||||
|
if iothub.lower() == "IotHub-CUBE-PROD.azure-devices.net".lower():
|
||||||
|
migration = "SAFT"
|
||||||
|
elif iothub.lower() == "iot-ingest-ess-prod.azure-devices.net".lower():
|
||||||
|
migration = "INOX"
|
||||||
|
else:
|
||||||
|
migration = "NONE"
|
||||||
|
|
||||||
|
device_id_match = re.search(r"deviceid\\=(.*?);", connection_string, re.IGNORECASE)
|
||||||
|
cloud_cube_id = device_id_match.group(1) if device_id_match else None
|
||||||
|
if cloud_cube_id.lower() == cube_id.lower():
|
||||||
|
status = "CORRECT"
|
||||||
|
else:
|
||||||
|
status = "INCORRECT"
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
migration = "NONE"
|
||||||
|
status = "INCORRECT"
|
||||||
|
finally:
|
||||||
|
print(f"{migration} {status}")
|
||||||
|
|
||||||
|
writer.writerow([i, ip_address, cube_id, migration, status])
|
||||||
|
file.flush()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -19,10 +19,10 @@ def resource_path(relative_path):
|
|||||||
dotenv_path = resource_path('.env')
|
dotenv_path = resource_path('.env')
|
||||||
load_dotenv(dotenv_path=dotenv_path)
|
load_dotenv(dotenv_path=dotenv_path)
|
||||||
|
|
||||||
ip_address_prefix = "10.188.10."
|
ip_address_prefix = "10.84.195."
|
||||||
ssh_command = "hostname"
|
ssh_command = "hostname"
|
||||||
|
|
||||||
csv_filename = "HOOHANA_01.csv"
|
csv_filename = "ANTWERP_01.csv"
|
||||||
|
|
||||||
ENV_SSH = {
|
ENV_SSH = {
|
||||||
"DEFAULT_CUBE_LINUX_ADMIN_USER": os.getenv("DEFAULT_CUBE_LINUX_ADMIN_USER"),
|
"DEFAULT_CUBE_LINUX_ADMIN_USER": os.getenv("DEFAULT_CUBE_LINUX_ADMIN_USER"),
|
||||||
@@ -91,7 +91,7 @@ def main():
|
|||||||
writer = csv.writer(file)
|
writer = csv.writer(file)
|
||||||
writer.writerow(["Number", "IP address", "Cube ID", "Environment", "Correct configuration"])
|
writer.writerow(["Number", "IP address", "Cube ID", "Environment", "Correct configuration"])
|
||||||
|
|
||||||
numbers = range(133, 157) #133 to 156
|
numbers = range(129, 139)
|
||||||
for i in numbers:
|
for i in numbers:
|
||||||
ip_address = f"{ip_address_prefix}{i}"
|
ip_address = f"{ip_address_prefix}{i}"
|
||||||
print(f"Activating SSH for {ip_address}:", end=" ")
|
print(f"Activating SSH for {ip_address}:", end=" ")
|
||||||
@@ -143,6 +143,12 @@ def main():
|
|||||||
migration = "NONE"
|
migration = "NONE"
|
||||||
status = "INCORRECT"
|
status = "INCORRECT"
|
||||||
|
|
||||||
|
try:
|
||||||
|
print(f"restarting cloud agent on {ip_address}")
|
||||||
|
restart_cloudagent(ip_address)
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
|
||||||
writer.writerow([i, ip_address, cube_id, migration, status])
|
writer.writerow([i, ip_address, cube_id, migration, status])
|
||||||
file.flush()
|
file.flush()
|
||||||
|
|
||||||
|
|||||||
336
Python/danish_batch_api copy.py
Normal file
336
Python/danish_batch_api copy.py
Normal file
@@ -0,0 +1,336 @@
|
|||||||
|
import pandas as pd
|
||||||
|
import requests
|
||||||
|
from urllib3.exceptions import InsecureRequestWarning
|
||||||
|
import jq
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
import time
|
||||||
|
from tqdm import tqdm
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
# Function to authenticate and get token
|
||||||
|
def authenticate(device_ip, payload):
|
||||||
|
auth_url = f"https://{device_ip}:8443/api/v1/auth"
|
||||||
|
try:
|
||||||
|
response = requests.post(auth_url, json=payload, verify=False)
|
||||||
|
if response.status_code == 200:
|
||||||
|
token = response.json()["data"]["token"]
|
||||||
|
#print(f"Authentication successful. Token received: {token}")
|
||||||
|
#print(" authenticated!")
|
||||||
|
return token
|
||||||
|
else:
|
||||||
|
print(f"Authentication failed. Status code: {response.status_code}")
|
||||||
|
return None
|
||||||
|
except Exception as e:
|
||||||
|
raise Exception("Authentication failed!") from e
|
||||||
|
|
||||||
|
# Function to update connection string through PATCH request
|
||||||
|
def update_connection_string(device_ip, token, connection_string):
|
||||||
|
headers = {
|
||||||
|
"mx-api-token": token
|
||||||
|
}
|
||||||
|
payload = {
|
||||||
|
"provisioning": {
|
||||||
|
"source": "manual",
|
||||||
|
"connectionString": connection_string,
|
||||||
|
"enable": True
|
||||||
|
}
|
||||||
|
}
|
||||||
|
patch_url = f"https://{device_ip}:8443/api/v1/azure-iotedge"
|
||||||
|
try:
|
||||||
|
response = requests.patch(patch_url, json=payload, headers=headers, verify=False)
|
||||||
|
if response.status_code == 200:
|
||||||
|
print(f"PATCH request successful for device {device_ip}")
|
||||||
|
else:
|
||||||
|
print(f"Failed to send PATCH request to device {device_ip}. Status code: {response.status_code}")
|
||||||
|
except Exception as e:
|
||||||
|
raise Exception("Update connection string failed!") from e
|
||||||
|
|
||||||
|
|
||||||
|
# Function to update NTP
|
||||||
|
def update_ntp(device_ip, token):
|
||||||
|
headers = {
|
||||||
|
"mx-api-token": token
|
||||||
|
}
|
||||||
|
payload = {
|
||||||
|
"ntp": {
|
||||||
|
"source": "timeserver",
|
||||||
|
"server": "10.84.171.254",
|
||||||
|
"enable": True
|
||||||
|
}
|
||||||
|
}
|
||||||
|
payload = {
|
||||||
|
"timezone": "America/Chicago"
|
||||||
|
}
|
||||||
|
patch_url = f"https://{device_ip}:8443/api/v1/device/time"
|
||||||
|
try:
|
||||||
|
response = requests.patch(patch_url, json=payload, headers=headers, verify=False)
|
||||||
|
if response.status_code == 200:
|
||||||
|
json_data = json.loads(response.content.decode())
|
||||||
|
time = json_data['data']['time']
|
||||||
|
timezone = json_data['data']['timezone']
|
||||||
|
last = json_data['data']['lastUpdateTime']
|
||||||
|
server = json_data['data']['ntp']['server']
|
||||||
|
enabled = json_data['data']['ntp']['enable']
|
||||||
|
print(time + " " + timezone + " " + last + " " + server + " " + str(enabled))
|
||||||
|
else:
|
||||||
|
json_data = json.loads(response.content.decode())
|
||||||
|
except Exception as e:
|
||||||
|
raise Exception("Update NTP failed!") from e
|
||||||
|
|
||||||
|
|
||||||
|
# Function to create an upgrade job
|
||||||
|
def create_upgrade_job(device_ip, token, upgrade_url):
|
||||||
|
headers = {
|
||||||
|
"mx-api-token": token
|
||||||
|
}
|
||||||
|
payload = {
|
||||||
|
"download": True,
|
||||||
|
"install": True,
|
||||||
|
"url": upgrade_url,
|
||||||
|
}
|
||||||
|
patch_url = f"https://{device_ip}:8443/api/v1/upgrades"
|
||||||
|
try:
|
||||||
|
response = requests.post(patch_url, json=payload, headers=headers, verify=False)
|
||||||
|
json_data = json.loads(response.content.decode())
|
||||||
|
id = json_data['data']['id']
|
||||||
|
return id
|
||||||
|
except Exception as e:
|
||||||
|
raise Exception("Create upgrade job failed") from e
|
||||||
|
|
||||||
|
# Function to get upgrade job
|
||||||
|
def get_upgrade_job(device_ip, token, id):
|
||||||
|
headers = {
|
||||||
|
"mx-api-token": token
|
||||||
|
}
|
||||||
|
payload = {
|
||||||
|
}
|
||||||
|
patch_url = f"https://{device_ip}:8443/api/v1/upgrades/{id}"
|
||||||
|
try:
|
||||||
|
response = requests.get(patch_url, json=payload, headers=headers, verify=False)
|
||||||
|
if response.status_code == 200:
|
||||||
|
json_data = json.loads(response.content.decode())
|
||||||
|
getid = json_data['data']['id']
|
||||||
|
created = json_data['data']['createdAt']
|
||||||
|
started = json_data['data']['startedAt']
|
||||||
|
cur_status = json_data['data']['state']
|
||||||
|
current_tasks = json_data['data']['completedTask']
|
||||||
|
total_tasks = json_data['data']['totalTask']
|
||||||
|
print(f"Upgrade job #{str(getid)} ({str(current_tasks)}/{str(total_tasks)}) {str(cur_status)}")
|
||||||
|
else:
|
||||||
|
print(f"Failed to send GET request to device {device_ip}. Status code: {response.status_code}")
|
||||||
|
print(response.content)
|
||||||
|
except Exception as e:
|
||||||
|
raise Exception("Failed getting upgrade job!") from e
|
||||||
|
|
||||||
|
def get_upgrade_jobs(device_ip, token):
|
||||||
|
headers = {
|
||||||
|
"mx-api-token": token
|
||||||
|
}
|
||||||
|
payload = {
|
||||||
|
}
|
||||||
|
patch_url = f"https://{device_ip}:8443/api/v1/upgrades"
|
||||||
|
try:
|
||||||
|
response = requests.get(patch_url, json=payload, headers=headers, verify=False)
|
||||||
|
if response.status_code == 200:
|
||||||
|
json_data = json.loads(response.content.decode())
|
||||||
|
count = json_data['count']
|
||||||
|
print(str(count))
|
||||||
|
for i in range(count):
|
||||||
|
getid = json_data['data'][i]['id']
|
||||||
|
created = json_data['data'][i]['createdAt']
|
||||||
|
cur_status = json_data['data'][i]['state']
|
||||||
|
current_tasks = json_data['data'][i]['completedTask']
|
||||||
|
total_tasks = json_data['data'][i]['totalTask']
|
||||||
|
print("JOB #" + str(getid) + " " + str(current_tasks) + "/" + str(total_tasks))
|
||||||
|
print("CREATED ON: " + str(created))
|
||||||
|
print("CURRENT STATUS: " + str(cur_status))
|
||||||
|
else:
|
||||||
|
print(f"Failed to send GET request to device {device_ip}. Status code: {response.status_code}")
|
||||||
|
print(response.content)
|
||||||
|
except Exception as e:
|
||||||
|
raise Exception("Failed getting all upgrade jobs!") from e
|
||||||
|
|
||||||
|
def get_last_upgrade_job(device_ip, token):
|
||||||
|
headers = {
|
||||||
|
"mx-api-token": token
|
||||||
|
}
|
||||||
|
payload = {
|
||||||
|
}
|
||||||
|
patch_url = f"https://{device_ip}:8443/api/v1/upgrades"
|
||||||
|
try:
|
||||||
|
response = requests.get(patch_url, json=payload, headers=headers, verify=False)
|
||||||
|
if response.status_code == 200:
|
||||||
|
json_data = json.loads(response.content.decode())
|
||||||
|
last_job = int(json_data['count'] - 1)
|
||||||
|
getid = json_data['data'][last_job]['id']
|
||||||
|
created = json_data['data'][last_job]['createdAt']
|
||||||
|
cur_status = json_data['data'][last_job]['state']
|
||||||
|
current_tasks = json_data['data'][last_job]['completedTask']
|
||||||
|
total_tasks = json_data['data'][last_job]['totalTask']
|
||||||
|
print("JOB #" + str(getid) + " " + str(current_tasks) + "/" + str(total_tasks))
|
||||||
|
print("CREATED ON: " + str(created))
|
||||||
|
print("CURRENT STATUS: " + str(cur_status))
|
||||||
|
for task in range(total_tasks):
|
||||||
|
if json_data['data'][last_job]['tasks'][task]['type'] == "download":
|
||||||
|
print(f"Downloaded: {json_data['data'][last_job]['tasks'][task]['progress']}%")
|
||||||
|
return getid
|
||||||
|
else:
|
||||||
|
print(f"Failed to send GET request to device {device_ip}. Status code: {response.status_code}")
|
||||||
|
print(response.content)
|
||||||
|
except Exception as e:
|
||||||
|
raise Exception("Failed getting last upgrade job!") from e
|
||||||
|
|
||||||
|
# Function to start upgrade job
|
||||||
|
def start_upgrade_job(device_ip, token, id):
|
||||||
|
headers = {
|
||||||
|
"mx-api-token": token
|
||||||
|
}
|
||||||
|
payload = {
|
||||||
|
}
|
||||||
|
patch_url = f"https://{device_ip}:8443/api/v1/upgrades/{id}/start"
|
||||||
|
try:
|
||||||
|
response = requests.put(patch_url, json=payload, headers=headers, verify=False)
|
||||||
|
if response.status_code == 200:
|
||||||
|
json_data = json.loads(response.content.decode())
|
||||||
|
curid = json_data['data']['id']
|
||||||
|
startedat = json_data['data']['startedAt']
|
||||||
|
print("Job #" + str(curid) + " started on " + str(startedat))
|
||||||
|
else:
|
||||||
|
print(f"Failed to send GET request to device {device_ip}. Status code: {response.status_code}")
|
||||||
|
print(response.content.decode())
|
||||||
|
except Exception as e:
|
||||||
|
raise Exception("Failed starting upgrade job!") from e
|
||||||
|
|
||||||
|
# Function to send a PUT request
|
||||||
|
def put_API(device_ip, token):
|
||||||
|
headers = {
|
||||||
|
"mx-api-token": token
|
||||||
|
}
|
||||||
|
payload = {
|
||||||
|
}
|
||||||
|
patch_url = "" #f"https://{device_ip}:8443/api/v1/upgrades/3/start"
|
||||||
|
try:
|
||||||
|
if patch_url == "":
|
||||||
|
raise Exception("Empty URL!")
|
||||||
|
response = requests.put(patch_url, json=payload, headers=headers, verify=False)
|
||||||
|
if response.status_code == 200:
|
||||||
|
json_data = json.loads(response.content.decode())
|
||||||
|
print(json_data['data']['firmwareVersion'])
|
||||||
|
else:
|
||||||
|
print(f"Failed to send GET request to device {device_ip}. Status code: {response.status_code}")
|
||||||
|
print(response.content.decode())
|
||||||
|
except Exception as e:
|
||||||
|
raise Exception("Failed sending PUT request!") from e
|
||||||
|
|
||||||
|
def get_version(device_ip, token):
|
||||||
|
headers = {
|
||||||
|
"mx-api-token": token
|
||||||
|
}
|
||||||
|
payload = {
|
||||||
|
}
|
||||||
|
patch_url = f"https://{device_ip}:8443/api/v1/device/general"
|
||||||
|
try:
|
||||||
|
response = requests.get(patch_url, json=payload, headers=headers, verify=False)
|
||||||
|
if response.status_code == 200:
|
||||||
|
json_data = json.loads(response.content.decode())
|
||||||
|
hostname = json_data['data']['hostName']
|
||||||
|
serial = json_data['data']['serialNumber']
|
||||||
|
version = json_data['data']['firmwareVersion']
|
||||||
|
description = json_data['data']['description']
|
||||||
|
print(hostname + " " + serial + " " + version + " " + description)
|
||||||
|
else:
|
||||||
|
print(f"Failed to send GET request to device {device_ip}. Status code: {response.status_code}")
|
||||||
|
print(response.content.decode())
|
||||||
|
except Exception as e:
|
||||||
|
raise Exception("Failed getting version!") from e
|
||||||
|
|
||||||
|
def get_time(device_ip, token):
|
||||||
|
headers = {
|
||||||
|
"mx-api-token": token
|
||||||
|
}
|
||||||
|
payload = {
|
||||||
|
}
|
||||||
|
patch_url = f"https://{device_ip}:8443/api/v1/device/time"
|
||||||
|
try:
|
||||||
|
response = requests.get(patch_url, json=payload, headers=headers, verify=False)
|
||||||
|
if response.status_code == 200:
|
||||||
|
json_data = json.loads(response.content.decode())
|
||||||
|
time = json_data['data']['time']
|
||||||
|
timezone = json_data['data']['timezone']
|
||||||
|
last = json_data['data']['lastUpdateTime']
|
||||||
|
print(time + " " + timezone + " " + last)
|
||||||
|
else:
|
||||||
|
print(f"Failed to send GET request to device {device_ip}. Status code: {response.status_code}")
|
||||||
|
print(response.content.decode())
|
||||||
|
except Exception as e:
|
||||||
|
raise Exception("Failed getting time!") from e
|
||||||
|
|
||||||
|
def delete_API(device_ip, token):
|
||||||
|
headers = {
|
||||||
|
"mx-api-token": token
|
||||||
|
}
|
||||||
|
payload = {
|
||||||
|
}
|
||||||
|
patch_url = "" #f"https://{device_ip}:8443/api/v1/upgrades/5"
|
||||||
|
try:
|
||||||
|
if patch_url == "":
|
||||||
|
raise Exception("Empty URL!")
|
||||||
|
response = requests.delete(patch_url, json=payload, headers=headers, verify=False)
|
||||||
|
print(response.status_code)
|
||||||
|
json_data = json.loads(response.content.decode())
|
||||||
|
json_str = json.dumps(json_data, indent=2)
|
||||||
|
print(json_str)
|
||||||
|
except Exception as e:
|
||||||
|
raise Exception("Failed sending DELETE request!") from e
|
||||||
|
|
||||||
|
def visual_wait(total_seconds):
|
||||||
|
# "total=total_seconds" sets the bar max value
|
||||||
|
# "bar_format" removes the default stats to keep it clean
|
||||||
|
with tqdm(total=total_seconds, bar_format="{desc} [{bar}]") as pbar:
|
||||||
|
# Loop backwards from 200 down to 1
|
||||||
|
for remaining in range(total_seconds, 0, -1):
|
||||||
|
# Manually update the text description to show the countdown
|
||||||
|
pbar.set_description_str(f"Waiting {remaining}s")
|
||||||
|
|
||||||
|
# Advance the visual bar by 1 step
|
||||||
|
pbar.update(1)
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
# Final update to show 0s at the very end
|
||||||
|
pbar.set_description_str(f"Finished waiting!")
|
||||||
|
|
||||||
|
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
|
||||||
|
|
||||||
|
default_user = str(os.getenv("DEFAULT_MOXA_USER"))
|
||||||
|
default_password = str(os.getenv("DEFAULT_MOXA_PASSWORD"))
|
||||||
|
moxa_range = [i for i in range(132, 159) ]
|
||||||
|
moxa_range.remove(137)
|
||||||
|
for i in moxa_range:
|
||||||
|
device_ip_address = str("10.84.157." + str(i))
|
||||||
|
print(device_ip_address)
|
||||||
|
|
||||||
|
payload_auth = {
|
||||||
|
"acceptEULA": True,
|
||||||
|
"name": default_user,
|
||||||
|
"password": default_password
|
||||||
|
}
|
||||||
|
|
||||||
|
upgrade_url = "http://10.84.157.137:8080/1.8.1.yaml"
|
||||||
|
|
||||||
|
try:
|
||||||
|
token = authenticate(device_ip_address, payload_auth)
|
||||||
|
if token:
|
||||||
|
get_version(device_ip_address, token)
|
||||||
|
get_last_upgrade_job(device_ip_address, token)
|
||||||
|
# id = create_upgrade_job(device_ip_address, token, upgrade_url)
|
||||||
|
# visual_wait(3)
|
||||||
|
# start_upgrade_job(device_ip_address, token, id)
|
||||||
|
# visual_wait(3)
|
||||||
|
# get_upgrade_job(device_ip_address, token, id)
|
||||||
|
# visual_wait(30)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Exception for {device_ip_address}: {e}")
|
||||||
|
continue
|
||||||
@@ -5,6 +5,7 @@ import jq
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
import time
|
||||||
|
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
@@ -84,6 +85,7 @@ def send_upgrade_request(device_ip, token, upgrade_url):
|
|||||||
patch_url = f"https://{device_ip}:8443/api/v1/upgrades"
|
patch_url = f"https://{device_ip}:8443/api/v1/upgrades"
|
||||||
response = requests.post(patch_url, json=payload, headers=headers, verify=False)
|
response = requests.post(patch_url, json=payload, headers=headers, verify=False)
|
||||||
json_data = json.loads(response.content.decode())
|
json_data = json.loads(response.content.decode())
|
||||||
|
#print(json.dumps(json_data, indent=4, sort_keys=True))
|
||||||
id = json_data['data']['id']
|
id = json_data['data']['id']
|
||||||
return id
|
return id
|
||||||
|
|
||||||
@@ -138,6 +140,38 @@ def get_upgrade_jobs(device_ip, token):
|
|||||||
print("JOB #" + str(getid) + " " + str(current_tasks) + "/" + str(total_tasks))
|
print("JOB #" + str(getid) + " " + str(current_tasks) + "/" + str(total_tasks))
|
||||||
print("CREATED ON: " + str(created))
|
print("CREATED ON: " + str(created))
|
||||||
print("CURRENT STATUS: " + str(cur_status))
|
print("CURRENT STATUS: " + str(cur_status))
|
||||||
|
print(json.dumps(json_data, indent=4, sort_keys=True))
|
||||||
|
else:
|
||||||
|
print(f"Failed to send GET request to device {device_ip}. Status code: {response.status_code}")
|
||||||
|
print(response.content)
|
||||||
|
|
||||||
|
def get_last_job(device_ip, token):
|
||||||
|
headers = {
|
||||||
|
"mx-api-token": token
|
||||||
|
}
|
||||||
|
payload = {
|
||||||
|
}
|
||||||
|
patch_url = f"https://{device_ip}:8443/api/v1/upgrades"
|
||||||
|
response = requests.get(patch_url, json=payload, headers=headers, verify=False)
|
||||||
|
if response.status_code == 200:
|
||||||
|
#print(f"GET request successful for device {device_ip}")
|
||||||
|
#print(json_str)
|
||||||
|
#print(json_data['data'][json_data['count'] - 1]['parameter']['url'], json_data['data'][json_data['count'] - 1]['state'], json_data['count'])
|
||||||
|
json_data = json.loads(response.content.decode())
|
||||||
|
last_job = int(json_data['count'] - 1)
|
||||||
|
getid = json_data['data'][last_job]['id']
|
||||||
|
created = json_data['data'][last_job]['createdAt']
|
||||||
|
cur_status = json_data['data'][last_job]['state']
|
||||||
|
current_tasks = json_data['data'][last_job]['completedTask']
|
||||||
|
total_tasks = json_data['data'][last_job]['totalTask']
|
||||||
|
print("JOB #" + str(getid) + " " + str(current_tasks) + "/" + str(total_tasks))
|
||||||
|
print("CREATED ON: " + str(created))
|
||||||
|
print("CURRENT STATUS: " + str(cur_status))
|
||||||
|
for task in range(total_tasks):
|
||||||
|
if json_data['data'][last_job]['tasks'][task]['type'] == "download":
|
||||||
|
print(f"Downloaded: {json_data['data'][last_job]['tasks'][task]['progress']}%")
|
||||||
|
#print(json.dumps(json_data, indent=4, sort_keys=True))
|
||||||
|
return getid
|
||||||
else:
|
else:
|
||||||
print(f"Failed to send GET request to device {device_ip}. Status code: {response.status_code}")
|
print(f"Failed to send GET request to device {device_ip}. Status code: {response.status_code}")
|
||||||
print(response.content)
|
print(response.content)
|
||||||
@@ -281,28 +315,47 @@ requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
|
|||||||
|
|
||||||
default_user = str(os.getenv("DEFAULT_MOXA_USER"))
|
default_user = str(os.getenv("DEFAULT_MOXA_USER"))
|
||||||
default_password = str(os.getenv("DEFAULT_MOXA_PASSWORD"))
|
default_password = str(os.getenv("DEFAULT_MOXA_PASSWORD"))
|
||||||
|
moxa_range = [i for i in range(148, 159) if i not in (136, 137)]
|
||||||
|
for i in moxa_range:
|
||||||
|
device_ip_address = str("10.84.157." + str(i))
|
||||||
|
print(device_ip_address, end=" ")
|
||||||
|
|
||||||
for i in range(193, 222):
|
|
||||||
upgrade_url = "https://files.thingsprocloud.com/package/Upgrade_AIG-301_2.4.0-4020_IMG_1.4_to_1.5.deb.yaml"
|
|
||||||
payload_auth = {
|
payload_auth = {
|
||||||
"acceptEULA": True,
|
"acceptEULA": True,
|
||||||
"name": default_user,
|
"name": default_user,
|
||||||
"password": default_password
|
"password": default_password
|
||||||
}
|
}
|
||||||
|
|
||||||
device_ip_address = str("10.84.171." + str(i))
|
|
||||||
|
|
||||||
#print(device_ip_address, end="")
|
|
||||||
token = authenticate(device_ip_address, payload_auth)
|
token = authenticate(device_ip_address, payload_auth)
|
||||||
|
print(hash(token))
|
||||||
|
|
||||||
|
upgrade_url = "http://10.84.157.137:8080/1.6.0.yaml"
|
||||||
|
|
||||||
if token:
|
if token:
|
||||||
#id = send_upgrade_request(device_ip_address,token,upgrade_url)
|
#send_upgrade_request(device_ip_address, token, upgrade_url)
|
||||||
#print(id)
|
id = get_last_job(device_ip_address, token)
|
||||||
#get_upgrade_job(device_ip_address, token, 6)
|
start_upgrade_job(device_ip_address, token, id)
|
||||||
#start_upgrade_job(device_ip_address, token, id)
|
time.sleep(300)
|
||||||
#put_API(device_ip_address, token)
|
#input("Continue?")
|
||||||
#patch_time(device_ip_address,token)
|
|
||||||
#get_time(device_ip_address, token)
|
|
||||||
#delete_API(device_ip_address,token)
|
|
||||||
get_API(device_ip_address, token)
|
|
||||||
else:
|
else:
|
||||||
print("Authentication failed!")
|
raise(Exception("Authentication failed!"))
|
||||||
|
|
||||||
|
# upgrade_url = "https://10.84.157.137/Upgrade_AIG-301_2.5.0-4404_IMG_1.5_to_1.6.0.yaml"
|
||||||
|
|
||||||
|
|
||||||
|
# device_ip_address = str("10.84.157." + str(i))
|
||||||
|
# print(f"{device_ip_address}")
|
||||||
|
|
||||||
|
# #print(device_ip_address, end="")
|
||||||
|
# token = authenticate(device_ip_address, payload_auth)
|
||||||
|
# if token:
|
||||||
|
# #id = send_upgrade_request(device_ip_address,token,upgrade_url)
|
||||||
|
# #print(id)
|
||||||
|
# get_last_job(device_ip_address, token)
|
||||||
|
# #start_upgrade_job(device_ip_address, token, id)
|
||||||
|
# #put_API(device_ip_address, token)
|
||||||
|
# #patch_time(device_ip_address,token)
|
||||||
|
# #get_time(device_ip_address, token)
|
||||||
|
# #delete_API(device_ip_address,token)
|
||||||
|
# #get_API(device_ip_address, token)
|
||||||
|
# else:
|
||||||
|
# print("Authentication failed!")
|
||||||
@@ -10,6 +10,28 @@ import os
|
|||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
# from 1 to 5 (Modbus)
|
||||||
|
# PCS
|
||||||
|
# AC_Resistance
|
||||||
|
# DC_Resistance
|
||||||
|
# Hz
|
||||||
|
# DCV
|
||||||
|
# VL1L2
|
||||||
|
# VL2L3
|
||||||
|
# VL3L1
|
||||||
|
|
||||||
|
# OnEvent
|
||||||
|
# ACSw_Status
|
||||||
|
# ActErrNoX
|
||||||
|
# DCSw_3_Status
|
||||||
|
# Fault_Id_tmp
|
||||||
|
# Fault_Status
|
||||||
|
# InvSt_tmp
|
||||||
|
# MV_Transformer_Gas_Monitoring
|
||||||
|
# MV_Transformer_Oil_Level_Trip
|
||||||
|
# MV_Transformer_Pressure
|
||||||
|
# MV_Transformer_Pressure_Trip
|
||||||
|
|
||||||
def generate_oe(device_id, modbus_tags):
|
def generate_oe(device_id, modbus_tags):
|
||||||
return {
|
return {
|
||||||
'description': '',
|
'description': '',
|
||||||
@@ -112,7 +134,6 @@ for device in devices:
|
|||||||
current_device_modules = registry_manager.get_modules(device.deviceId)
|
current_device_modules = registry_manager.get_modules(device.deviceId)
|
||||||
for module in current_device_modules:
|
for module in current_device_modules:
|
||||||
if (module.module_id == module_id):
|
if (module.module_id == module_id):
|
||||||
#print(f"{module.module_id}")
|
|
||||||
device.setModule(module)
|
device.setModule(module)
|
||||||
payload = '{"method":"GET", "path":"/azure-iotedge/messages"}'
|
payload = '{"method":"GET", "path":"/azure-iotedge/messages"}'
|
||||||
try:
|
try:
|
||||||
@@ -120,7 +141,6 @@ for device in devices:
|
|||||||
response = registry_manager.invoke_device_module_method(device_id=device.deviceId, module_id=module_id, direct_method_request=direct_method)
|
response = registry_manager.invoke_device_module_method(device_id=device.deviceId, module_id=module_id, direct_method_request=direct_method)
|
||||||
device_messages = json.dumps(response.payload, indent = 2)
|
device_messages = json.dumps(response.payload, indent = 2)
|
||||||
parsed_messages = json.loads(device_messages)
|
parsed_messages = json.loads(device_messages)
|
||||||
#print("Parsed messages")
|
|
||||||
message_ids_with_tags = {}
|
message_ids_with_tags = {}
|
||||||
tags_to_on_event = defaultdict(set)
|
tags_to_on_event = defaultdict(set)
|
||||||
for message in parsed_messages["data"]:
|
for message in parsed_messages["data"]:
|
||||||
@@ -131,6 +151,7 @@ for device in devices:
|
|||||||
|
|
||||||
tags = message.get("tags", {}).get("modbus_tcp_master", {})
|
tags = message.get("tags", {}).get("modbus_tcp_master", {})
|
||||||
|
|
||||||
|
# This block seems list existing OE topics and their content.
|
||||||
for asset, tag_list in tags.items():
|
for asset, tag_list in tags.items():
|
||||||
if "InvSt" in tag_list and "OE" in topic_name:
|
if "InvSt" in tag_list and "OE" in topic_name:
|
||||||
message_ids_with_tags.setdefault(message_id, message)
|
message_ids_with_tags.setdefault(message_id, message)
|
||||||
@@ -140,68 +161,69 @@ for device in devices:
|
|||||||
message_ids_with_tags.setdefault(message_id, message)
|
message_ids_with_tags.setdefault(message_id, message)
|
||||||
tags_to_on_event[asset].add("Fault_Id")
|
tags_to_on_event[asset].add("Fault_Id")
|
||||||
print(f"{device.getDeviceId()} {device.getSite()} {device.getNumber()} {message_id} {topic_name} {asset} Fault_Id")
|
print(f"{device.getDeviceId()} {device.getSite()} {device.getNumber()} {message_id} {topic_name} {asset} Fault_Id")
|
||||||
# print(f"{device.getDeviceId()} {device.getSite()} {device.getNumber()}")
|
|
||||||
# print(f"Messages to update: {', '.join(str(key) for key in message_ids_with_tags.keys())}")
|
|
||||||
# for asset, tag in tags_to_on_event.items():
|
|
||||||
# print(f"Alarms to make on event:")
|
|
||||||
# print(f"{asset}: {', '.join(tag)}")
|
|
||||||
# print(f"Finishing on {device}")
|
|
||||||
|
|
||||||
# oe_exists = False
|
print(f"{device.getDeviceId()} {device.getSite()} {device.getNumber()}")
|
||||||
# twelve_exists = False
|
print(f"Messages to update: {', '.join(str(key) for key in message_ids_with_tags.keys())}")
|
||||||
|
for asset, tag in tags_to_on_event.items():
|
||||||
|
print(f"Alarms to make on event:")
|
||||||
|
print(f"{asset}: {', '.join(tag)}")
|
||||||
|
print(f"Finishing on {device}")
|
||||||
|
|
||||||
# for message in message_ids_with_tags.values():
|
oe_exists = False
|
||||||
# if message['outputTopic'] == "REL_OE_PCS":
|
twelve_exists = False
|
||||||
# oe_exists = True
|
|
||||||
# if message['outputTopic'] == "REL_OE_PCS_12H":
|
|
||||||
# twelve_exists = True
|
|
||||||
|
|
||||||
# if (oe_exists == False or twelve_exists == False):
|
for message in message_ids_with_tags.values():
|
||||||
# first_message = next(iter(message_ids_with_tags.values()))
|
if message['outputTopic'] == "REL_OE_PCS":
|
||||||
# properties = first_message['properties']
|
oe_exists = True
|
||||||
# cdid = next((item['value'] for item in properties if item['key'] == 'cdid'))
|
if message['outputTopic'] == "REL_OE_PCS_12H":
|
||||||
# print(f"{cdid}")
|
twelve_exists = True
|
||||||
|
|
||||||
# modbus_tags = {
|
if (oe_exists == False or twelve_exists == False):
|
||||||
# asset: sorted(list(tags))
|
first_message = next(iter(message_ids_with_tags.values()))
|
||||||
# for asset, tags in tags_to_on_event.items()
|
properties = first_message['properties']
|
||||||
# }
|
cdid = next((item['value'] for item in properties if item['key'] == 'cdid'))
|
||||||
# new_payload = ""
|
print(f"{cdid}")
|
||||||
# if (oe_exists == False):
|
|
||||||
# new_payload = '{"method":"POST", "path":"/azure-iotedge/messages", "requestBody":' + json.dumps(generate_oe(cdid, modbus_tags)) + '}'
|
|
||||||
# new_direct_method = CloudToDeviceMethod(method_name=method_name, payload=json.loads(new_payload))
|
|
||||||
# new_response = registry_manager.invoke_device_module_method(device_id=device.deviceId, module_id=module_id, direct_method_request=new_direct_method)
|
|
||||||
# print(json.dumps(new_response.payload, indent = 2))
|
|
||||||
|
|
||||||
# if (twelve_exists == False):
|
modbus_tags = {
|
||||||
# new_payload = '{"method":"POST", "path":"/azure-iotedge/messages", "requestBody":' + json.dumps(generate_12h(cdid, modbus_tags)) + '}'
|
asset: sorted(list(tags))
|
||||||
# new_direct_method = CloudToDeviceMethod(method_name=method_name, payload=json.loads(new_payload))
|
for asset, tags in tags_to_on_event.items()
|
||||||
# new_response = registry_manager.invoke_device_module_method(device_id=device.deviceId, module_id=module_id, direct_method_request=new_direct_method)
|
}
|
||||||
# print(json.dumps(new_response.payload, indent = 2))
|
new_payload = ""
|
||||||
|
if (oe_exists == False):
|
||||||
|
new_payload = '{"method":"POST", "path":"/azure-iotedge/messages", "requestBody":' + json.dumps(generate_oe(cdid, modbus_tags)) + '}'
|
||||||
|
new_direct_method = CloudToDeviceMethod(method_name=method_name, payload=json.loads(new_payload))
|
||||||
|
new_response = registry_manager.invoke_device_module_method(device_id=device.deviceId, module_id=module_id, direct_method_request=new_direct_method)
|
||||||
|
print(json.dumps(new_response.payload, indent = 2))
|
||||||
|
|
||||||
# for id, message in message_ids_with_tags.items():
|
if (twelve_exists == False):
|
||||||
# if "OE" in message['outputTopic']:
|
new_payload = '{"method":"POST", "path":"/azure-iotedge/messages", "requestBody":' + json.dumps(generate_12h(cdid, modbus_tags)) + '}'
|
||||||
# continue
|
new_direct_method = CloudToDeviceMethod(method_name=method_name, payload=json.loads(new_payload))
|
||||||
# tags_to_remove = {"InvSt", "Fault_Id"}
|
new_response = registry_manager.invoke_device_module_method(device_id=device.deviceId, module_id=module_id, direct_method_request=new_direct_method)
|
||||||
# for protocol, versions in message['tags'].items():
|
print(json.dumps(new_response.payload, indent = 2))
|
||||||
# for version, tag_list in versions.items():
|
|
||||||
# message['tags'][protocol][version] = [
|
for id, message in message_ids_with_tags.items():
|
||||||
# tag for tag in tag_list if tag not in tags_to_remove
|
if "OE" in message['outputTopic']:
|
||||||
# ]
|
continue
|
||||||
# print("\n\n\nREMOVING\n\n\n")
|
tags_to_remove = {"InvSt", "Fault_Id"}
|
||||||
# new_payload = '{"method":"PUT", "path":"/azure-iotedge/messages/' + str(id) + '", "requestBody":' + json.dumps(message) + '}'
|
for protocol, versions in message['tags'].items():
|
||||||
# print(new_payload)
|
for version, tag_list in versions.items():
|
||||||
# new_direct_method = CloudToDeviceMethod(method_name=method_name, payload=json.loads(new_payload))
|
message['tags'][protocol][version] = [
|
||||||
# new_response = registry_manager.invoke_device_module_method(device_id=device.deviceId, module_id=module_id, direct_method_request=new_direct_method)
|
tag for tag in tag_list if tag not in tags_to_remove
|
||||||
# print(json.dumps(new_response.payload, indent = 2))
|
]
|
||||||
# print(message['tags'])
|
print("\n\n\nREMOVING\n\n\n")
|
||||||
|
new_payload = '{"method":"PUT", "path":"/azure-iotedge/messages/' + str(id) + '", "requestBody":' + json.dumps(message) + '}'
|
||||||
|
print(new_payload)
|
||||||
|
new_direct_method = CloudToDeviceMethod(method_name=method_name, payload=json.loads(new_payload))
|
||||||
|
new_response = registry_manager.invoke_device_module_method(device_id=device.deviceId, module_id=module_id, direct_method_request=new_direct_method)
|
||||||
|
print(json.dumps(new_response.payload, indent = 2))
|
||||||
|
print(message['tags'])
|
||||||
|
|
||||||
|
|
||||||
# print("\n\n\nREMOVING\n\n\n")
|
print("\n\n\nREMOVING\n\n\n")
|
||||||
# new_payload = '{"method":"PUT", "path":"/azure-iotedge/messages/' + str(id) + '", "requestBody":' + json.dumps(message) + '}'
|
new_payload = '{"method":"PUT", "path":"/azure-iotedge/messages/' + str(id) + '", "requestBody":' + json.dumps(message) + '}'
|
||||||
# print(new_payload)
|
print(new_payload)
|
||||||
# new_direct_method = CloudToDeviceMethod(method_name=method_name, payload=json.loads(new_payload))
|
new_direct_method = CloudToDeviceMethod(method_name=method_name, payload=json.loads(new_payload))
|
||||||
# new_response = registry_manager.invoke_device_module_method(device_id=device.deviceId, module_id=module_id, direct_method_request=new_direct_method)
|
new_response = registry_manager.invoke_device_module_method(device_id=device.deviceId, module_id=module_id, direct_method_request=new_direct_method)
|
||||||
# print(json.dumps(new_response.payload, indent = 2))
|
print(json.dumps(new_response.payload, indent = 2))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(str(e))
|
logger.error(str(e))
|
||||||
366
Python/on_event_topics_2.py
Normal file
366
Python/on_event_topics_2.py
Normal file
@@ -0,0 +1,366 @@
|
|||||||
|
from azure.iot.hub import IoTHubRegistryManager
|
||||||
|
from azure.iot.hub.protocol.models import QuerySpecification, Module
|
||||||
|
from azure.iot.hub.models import CloudToDeviceMethod, CloudToDeviceMethodResult
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from isight_device import iSightDevice
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
import copy
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
import re
|
||||||
|
|
||||||
|
# from 1 to 5 (Modbus)
|
||||||
|
# PCS
|
||||||
|
# AC_Resistance
|
||||||
|
# DC_Resistance
|
||||||
|
# Hz
|
||||||
|
# DCV
|
||||||
|
# VL1L2
|
||||||
|
# VL2L3
|
||||||
|
# VL3L1
|
||||||
|
|
||||||
|
def resolve_missing_topic(topic_name, available_list):
|
||||||
|
"""
|
||||||
|
Prompts user to select a topic or create a new one.
|
||||||
|
Returns the selected ID or 0 if creation is requested.
|
||||||
|
"""
|
||||||
|
print(f"Topic {topic_name} not found!")
|
||||||
|
print(f"0. Create a new {topic_name} topic")
|
||||||
|
|
||||||
|
# Display available options
|
||||||
|
for idx, item in enumerate(available_list):
|
||||||
|
print(f"{idx + 1}. {item['outputTopic']}")
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
choice = int(input(f"Select an option for {topic_name}: "))
|
||||||
|
|
||||||
|
if choice == 0:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if 1 <= choice <= len(available_list):
|
||||||
|
selected = available_list[choice - 1]
|
||||||
|
return selected['id']
|
||||||
|
|
||||||
|
print("Invalid number, please select from the list.")
|
||||||
|
except ValueError:
|
||||||
|
print("Please enter a valid number.")
|
||||||
|
|
||||||
|
def generate_oe(device_id, modbus_tags):
|
||||||
|
return {
|
||||||
|
'description': '',
|
||||||
|
'outputTopic': 'REL_OE_PCS',
|
||||||
|
'properties': [
|
||||||
|
{'key': 'cdid', 'value': device_id},
|
||||||
|
{'key': 'deviceType', 'value': 'AC_GATEWAY'}
|
||||||
|
],
|
||||||
|
'tags': {
|
||||||
|
'modbus_tcp_master': modbus_tags
|
||||||
|
},
|
||||||
|
'sendOutThreshold': {
|
||||||
|
'mode': 'immediately',
|
||||||
|
'size': 4096,
|
||||||
|
'time': 60,
|
||||||
|
'sizeIdleTimer': {
|
||||||
|
'enable': True,
|
||||||
|
'time': 60
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'minPublishInterval': 0,
|
||||||
|
'samplingMode': 'allChangedValues',
|
||||||
|
'customSamplingRate': False,
|
||||||
|
'pollingInterval': 0,
|
||||||
|
'onChange': True,
|
||||||
|
'enable': True,
|
||||||
|
'format': (
|
||||||
|
'{deviceId:("' + device_id +
|
||||||
|
'"),emissionDate:(now|todateiso8601),deviceType:("AC_GATEWAY"),'
|
||||||
|
'metrics:[{idAsset:(.srcName),name:(.tagName),value:.dataValue,'
|
||||||
|
'timestamp:((.ts/1000000)|todateiso8601)}]}'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def generate_12h(device_id, modbus_tags):
|
||||||
|
return {
|
||||||
|
'description': '',
|
||||||
|
'outputTopic': 'REL_OE_PCS_12H',
|
||||||
|
'properties': [
|
||||||
|
{'key': 'cdid', 'value': device_id},
|
||||||
|
{'key': 'deviceType', 'value': 'AC_GATEWAY'}
|
||||||
|
],
|
||||||
|
'tags': {
|
||||||
|
'modbus_tcp_master': modbus_tags
|
||||||
|
},
|
||||||
|
'sendOutThreshold': {
|
||||||
|
'mode': 'byTime',
|
||||||
|
'size': 4096,
|
||||||
|
'time': 43200,
|
||||||
|
'sizeIdleTimer': {
|
||||||
|
'enable': True,
|
||||||
|
'time': 60
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'minPublishInterval': 0,
|
||||||
|
'samplingMode': 'latestValues',
|
||||||
|
'customSamplingRate': False,
|
||||||
|
'pollingInterval': 43200,
|
||||||
|
'onChange': False,
|
||||||
|
'enable': True,
|
||||||
|
'format': (
|
||||||
|
'{deviceId:("' + device_id +
|
||||||
|
'"),emissionDate:(now|todateiso8601),deviceType:("AC_GATEWAY"),'
|
||||||
|
'metrics:[{idAsset:(.srcName),name:(.tagName),value:.dataValue,'
|
||||||
|
'timestamp:((.ts/1000000)|todateiso8601)}]}'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Configure logger to use stderr
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.ERROR,
|
||||||
|
format='%(asctime)s - %(levelname)s - %(message)s',
|
||||||
|
stream=sys.stderr # <- Key line
|
||||||
|
)
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
# .env containing secrets to authorize access to IoT Hub
|
||||||
|
load_dotenv()
|
||||||
|
CONNECTION_STRING = str(os.getenv("CONNECTION_STRING_INOX_PROD"))
|
||||||
|
if CONNECTION_STRING == "":
|
||||||
|
print("Provide a connection string for the Iot Hub before running the script!")
|
||||||
|
exit(13)
|
||||||
|
|
||||||
|
# IoT Edge module and method that allow Moxa AIG-301 management
|
||||||
|
module_id = "thingspro-agent"
|
||||||
|
method_name = "thingspro-api-v1"
|
||||||
|
|
||||||
|
# Connection to IoT Hub and listing of IoT Edge device (so AIG-301 in the scope of SAFT)
|
||||||
|
registry_manager = IoTHubRegistryManager.from_connection_string(CONNECTION_STRING)
|
||||||
|
query_spec = QuerySpecification(query="SELECT * FROM devices WHERE capabilities.iotEdge = true ")
|
||||||
|
query_result = registry_manager.query_iot_hub(query_spec)
|
||||||
|
|
||||||
|
# Sorting devices by site
|
||||||
|
devices = []
|
||||||
|
try:
|
||||||
|
for item in query_result.items:
|
||||||
|
if str(item.connection_state) == "Connected":
|
||||||
|
currentDevice = iSightDevice(str(item.device_id), str(item.tags['site']), int(item.tags['number']), str(item.tags['version']))
|
||||||
|
devices.append(iSightDevice(str(item.device_id), str(item.tags['site']), int(item.tags['number']), str(item.tags['version'])))
|
||||||
|
devices.sort(key = lambda d: (d.site, d.number))
|
||||||
|
else:
|
||||||
|
print(f"Device not connected: {str(item.tags['site'])} {int(item.tags['number'])} {str(item.device_id)}")
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
# This block runs when user presses Ctrl+C
|
||||||
|
print("\nProgram killed by user (Ctrl+C).")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
# List of the tags that are a status or alert
|
||||||
|
on_event_tags = [
|
||||||
|
"1365",
|
||||||
|
"AC_Insulation_Status",
|
||||||
|
"ACSw_Status",
|
||||||
|
"ActErrNo1",
|
||||||
|
"ActErrNo10",
|
||||||
|
"ActErrNo2",
|
||||||
|
"ActErrNo3",
|
||||||
|
"ActErrNo4",
|
||||||
|
"ActErrNo5",
|
||||||
|
"ActErrNo6",
|
||||||
|
"ActErrNo7",
|
||||||
|
"ActErrNo8",
|
||||||
|
"ActErrNo9",
|
||||||
|
"BESS_Control_Mode",
|
||||||
|
"BESS_Control_Mode_2",
|
||||||
|
"Calibration_Required",
|
||||||
|
"CO_Sensor_1",
|
||||||
|
"CO_Sensor_2",
|
||||||
|
"CO_Sensor_3",
|
||||||
|
"CO_Sensor_4",
|
||||||
|
"CO_Sensor_5",
|
||||||
|
"Combi_Detector_1",
|
||||||
|
"Combi_Detector_2",
|
||||||
|
"Combi_Detector_3",
|
||||||
|
"Combi_Detector_4",
|
||||||
|
"Combi_Detector_5",
|
||||||
|
"Combi_Detector_6",
|
||||||
|
"DC_Insulation_Status",
|
||||||
|
"DCSw_1_Status",
|
||||||
|
"DCSw_2_Status",
|
||||||
|
"DCSw_3_Status",
|
||||||
|
"Digital_Input_Line_0",
|
||||||
|
"DIL0",
|
||||||
|
"DIL1",
|
||||||
|
"DIL2",
|
||||||
|
"Door_Open",
|
||||||
|
"EMS_Control",
|
||||||
|
"Energy_Fault",
|
||||||
|
"ESS_Status",
|
||||||
|
"Extinguisher_Activated",
|
||||||
|
"Fault_Id_tmp",
|
||||||
|
"Fault_Module_Id",
|
||||||
|
"Fault_Status",
|
||||||
|
"General_Fault",
|
||||||
|
"InvSt_tmp",
|
||||||
|
"Last_Event",
|
||||||
|
"Loss_of_Agent",
|
||||||
|
"Manual_mode",
|
||||||
|
"Manual_Pull_Station",
|
||||||
|
"MV_Transformer_Gas_Monitoring",
|
||||||
|
"MV_Transformer_Oil_Level_Trip",
|
||||||
|
"MV_Transformer_Pressure",
|
||||||
|
"MV_Transformer_Pressure_Trip",
|
||||||
|
"MV_Transformer_Temperature_Alarm",
|
||||||
|
"MV_Transformer_Temperature_Warning",
|
||||||
|
"NbModule",
|
||||||
|
"NbModule_Run",
|
||||||
|
"Reg_Request",
|
||||||
|
"Version",
|
||||||
|
"Warn_Id"
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
for device in devices:
|
||||||
|
|
||||||
|
print(f"{device.getSite()} {device.getNumber()} {device.getDeviceId()}")
|
||||||
|
|
||||||
|
modbus_tags_to_set_on_event = {}
|
||||||
|
id_on_event_topic = -1
|
||||||
|
id_12h_topic = -1
|
||||||
|
|
||||||
|
current_device_modules = registry_manager.get_modules(device.deviceId)
|
||||||
|
for module in current_device_modules:
|
||||||
|
if (module.module_id == module_id):
|
||||||
|
device.setModule(module)
|
||||||
|
|
||||||
|
# This block get all the Modbus tags available and cross check them with the list of tags that require changes
|
||||||
|
payload = '{"method":"GET", "path":"/tags/list"}'
|
||||||
|
try:
|
||||||
|
direct_method = CloudToDeviceMethod(method_name=method_name, payload=json.loads(payload))
|
||||||
|
response = registry_manager.invoke_device_module_method(device_id=device.deviceId, module_id=module_id, direct_method_request=direct_method)
|
||||||
|
device_tags = json.dumps(response.payload, indent = 2)
|
||||||
|
parsed_tags = json.loads(device_tags)
|
||||||
|
|
||||||
|
for item in parsed_tags['data']:
|
||||||
|
if item['prvdName'] == "modbus_tcp_master" and item['tagName'] in on_event_tags:
|
||||||
|
modbus_tags_to_set_on_event.setdefault(item['srcName'], []).append(item['tagName'])
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Exception when getting list of device's Modbus tags that can be set On Event:\n{str(e)}")
|
||||||
|
|
||||||
|
|
||||||
|
payload = '{"method":"GET", "path":"/azure-iotedge/messages"}'
|
||||||
|
try:
|
||||||
|
direct_method = CloudToDeviceMethod(method_name=method_name, payload=json.loads(payload))
|
||||||
|
response = registry_manager.invoke_device_module_method(device_id=device.deviceId, module_id=module_id, direct_method_request=direct_method)
|
||||||
|
device_topics = json.dumps(response.payload, indent = 2)
|
||||||
|
parsed_topics = json.loads(device_topics)
|
||||||
|
|
||||||
|
for item in parsed_topics["data"]:
|
||||||
|
if item['enable'] and item["outputTopic"] == "REL_OE_PCS":
|
||||||
|
id_on_event_topic = item['id']
|
||||||
|
if item['enable'] and item["outputTopic"] == "REL_OE_PCS_12H":
|
||||||
|
id_12h_topic = item['id']
|
||||||
|
|
||||||
|
available_topics = [
|
||||||
|
item for item in parsed_topics["data"]
|
||||||
|
if item['enable']
|
||||||
|
and item['id'] != id_on_event_topic
|
||||||
|
and item['id'] != id_12h_topic
|
||||||
|
]
|
||||||
|
|
||||||
|
if id_on_event_topic == -1:
|
||||||
|
id_on_event_topic = resolve_missing_topic("REL_OE_PCS", available_topics)
|
||||||
|
|
||||||
|
if id_12h_topic == -1:
|
||||||
|
id_12h_topic = resolve_missing_topic("REL_OE_PCS_12H", available_topics)
|
||||||
|
|
||||||
|
|
||||||
|
targets = [
|
||||||
|
{"id": id_on_event_topic, "name": "OnEvent"},
|
||||||
|
{"id": id_12h_topic, "name": "12h"}
|
||||||
|
]
|
||||||
|
|
||||||
|
for target in targets:
|
||||||
|
target_id = target["id"]
|
||||||
|
target_name = target["name"]
|
||||||
|
|
||||||
|
if target_id > 0:
|
||||||
|
topic_item = next((item for item in parsed_topics["data"] if item["id"] == target_id), None)
|
||||||
|
if topic_item:
|
||||||
|
original_tags = topic_item.get("tags", {}).get("modbus_tcp_master", {})
|
||||||
|
else: # Pure error handling, detected ID does not exist
|
||||||
|
print(f"Error: ID {target_id} not found. Skipping.")
|
||||||
|
continue
|
||||||
|
else: # Not error handling, target_id == 0 means creation of new topic
|
||||||
|
original_tags = {}
|
||||||
|
|
||||||
|
final_tag_list = copy.deepcopy(original_tags)
|
||||||
|
for device_name, tags_to_add in modbus_tags_to_set_on_event.items():
|
||||||
|
if device_name not in final_tag_list:
|
||||||
|
final_tag_list[device_name] = []
|
||||||
|
current_list = final_tag_list[device_name]
|
||||||
|
for tag in tags_to_add:
|
||||||
|
if tag not in current_list:
|
||||||
|
current_list.append(tag)
|
||||||
|
# CASE A: CREATE NEW
|
||||||
|
if target_id == 0:
|
||||||
|
print(f"[{target_name}] ID is 0 -> ACTION: CREATE NEW TOPIC")
|
||||||
|
elif final_tag_list != original_tags:
|
||||||
|
print(f"[{target_name}] ID {target_id} -> ACTION: UPDATE EXISTING")
|
||||||
|
else:
|
||||||
|
print(f"[{target_name}] ID {target_id} -> ACTION: NONE (Already up to date)")
|
||||||
|
|
||||||
|
# Removing tags from existing topics: cleaned_topic
|
||||||
|
protected_ids = [id_on_event_topic, id_12h_topic]
|
||||||
|
|
||||||
|
|
||||||
|
for item in parsed_topics["data"]:
|
||||||
|
if item['id'] in protected_ids or item['enable'] == False:
|
||||||
|
continue
|
||||||
|
|
||||||
|
original_tags = item.get("tags", {}).get("modbus_tcp_master", {})
|
||||||
|
|
||||||
|
try:
|
||||||
|
idAsset = re.search(r"idAsset\s*:\s*\((.*?)\)", item.get("format", "")).group(1).strip()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Exception while getting idAsset: {item}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not original_tags:
|
||||||
|
continue
|
||||||
|
|
||||||
|
final_tag_list = copy.deepcopy(original_tags)
|
||||||
|
modification_needed = False
|
||||||
|
|
||||||
|
for device_name, tags_to_remove in modbus_tags_to_set_on_event.items():
|
||||||
|
|
||||||
|
if device_name in final_tag_list:
|
||||||
|
current_list = final_tag_list[device_name]
|
||||||
|
new_list = [t for t in current_list if t not in tags_to_remove]
|
||||||
|
|
||||||
|
if len(new_list) != len(current_list):
|
||||||
|
final_tag_list[device_name] = new_list
|
||||||
|
modification_needed = True
|
||||||
|
|
||||||
|
if not final_tag_list[device_name]:
|
||||||
|
del final_tag_list[device_name]
|
||||||
|
|
||||||
|
if modification_needed:
|
||||||
|
print(f"-> Applying update for topic: {item['outputTopic']} (ID: {item['id']} & {idAsset})")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Exception when getting all Telemetry Topics:\n{str(e)}")
|
||||||
|
|
||||||
|
user_input = 'Y' # input("Continue? Y/n ").lower().strip()
|
||||||
|
if user_input == 'n':
|
||||||
|
print("Stopping loop.")
|
||||||
|
break
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
# This block runs when user presses Ctrl+C
|
||||||
|
print("\nProgram killed by user (Ctrl+C).")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
@@ -11,3 +11,4 @@ fabric
|
|||||||
ruamel.yaml
|
ruamel.yaml
|
||||||
netmiko
|
netmiko
|
||||||
pexpect
|
pexpect
|
||||||
|
tqdm
|
||||||
@@ -175,6 +175,14 @@ def find_config_value(config_content, option):
|
|||||||
# If the loop finishes without finding the option, return None
|
# If the loop finishes without finding the option, return None
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def cloud_configuration_csv(result):
|
||||||
|
lightTelemetry = find_config_value(result, "light-telemetry")
|
||||||
|
telemetryOn = find_config_value(result, "telemetry-on")
|
||||||
|
compressionEnabled = find_config_value(result, "compression-enabled")
|
||||||
|
remoteUpdateOn = find_config_value(result, "remote-update-on")
|
||||||
|
connectionString = find_config_value(result, "connection-string")
|
||||||
|
return f"{lightTelemetry};{telemetryOn};{compressionEnabled};{remoteUpdateOn};{connectionString};"
|
||||||
|
|
||||||
def cloud_configuration_check(hostname, result, iot_hub, proxy_host, proxy_port):
|
def cloud_configuration_check(hostname, result, iot_hub, proxy_host, proxy_port):
|
||||||
print(f"\tLight telemetry:", end=" ", flush=True)
|
print(f"\tLight telemetry:", end=" ", flush=True)
|
||||||
status = find_config_value(result, "light-telemetry")
|
status = find_config_value(result, "light-telemetry")
|
||||||
@@ -185,7 +193,7 @@ def cloud_configuration_check(hostname, result, iot_hub, proxy_host, proxy_port)
|
|||||||
|
|
||||||
print(f"\tTelemetry:", end=" ", flush=True)
|
print(f"\tTelemetry:", end=" ", flush=True)
|
||||||
status = find_config_value(result, "telemetry-on")
|
status = find_config_value(result, "telemetry-on")
|
||||||
if status == "true":
|
if status == "False":
|
||||||
print(f"✅", end="\n", flush=True)
|
print(f"✅", end="\n", flush=True)
|
||||||
else:
|
else:
|
||||||
print(f"❌")
|
print(f"❌")
|
||||||
@@ -437,12 +445,55 @@ def write_remote_config_base64_sudo(c, remote_path, content, sudo_pass, user_own
|
|||||||
# Re-raise the exception for the main loop.
|
# Re-raise the exception for the main loop.
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
def check_for_specific_curl_error(c):
|
||||||
|
|
||||||
|
def execute_command(c, command):
|
||||||
|
"""Executes a simple command on the remote device."""
|
||||||
|
try:
|
||||||
|
result = c.run(command, hide=True)
|
||||||
|
return result.stdout
|
||||||
|
except Exception as e:
|
||||||
|
raise
|
||||||
|
|
||||||
|
"""
|
||||||
|
Checks for the specific cURL exit code 35.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
c: The connection object.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True if the expected error is caught, False otherwise.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# We call execute_command, but expect it to fail and raise an exception
|
||||||
|
result = execute_command(c, "curl -m 15 -x https://10.81.35.126:8080 https://iot-ingest-ess-prod.azure-devices.net")
|
||||||
|
|
||||||
|
# If the command somehow succeeds, the expected error did not occur.
|
||||||
|
print(f"Success (unexpected): {result.strip()}", end="\n", flush=True)
|
||||||
|
return False
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
# The command failed as expected. Now, check if it's the RIGHT failure.
|
||||||
|
error_message = str(e)
|
||||||
|
|
||||||
|
# Check for the unique identifiers of your expected error.
|
||||||
|
is_exit_code_35 = "Exit code: 35" in error_message
|
||||||
|
is_ssl_version_error = "wrong version number" in error_message
|
||||||
|
|
||||||
|
if is_exit_code_35 and is_ssl_version_error:
|
||||||
|
# This is the exact error you were expecting.
|
||||||
|
# print("Caught expected cURL error (Exit code 35, SSL wrong version number).")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
# This is a different, unexpected error.
|
||||||
|
print(f"\n[cURL] An unexpected exception occurred: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""Main function to parse arguments and orchestrate tasks."""
|
"""Main function to parse arguments and orchestrate tasks."""
|
||||||
ip_address_prefix = "10.81.56." # DK2 subnet
|
ip_address_prefix = "10.84.165." # DK2 subnet
|
||||||
ip_address_range = list(range(129, 145)) # From 129 to 144 (16 CUBEs)
|
ip_address_range = list(range(131, 188)) # From 129 to 144 (16 CUBEs)
|
||||||
# ip_address_range.append(72) # Add 85 after 74.
|
# ip_address_range.append(85) # Add 85 after 74.
|
||||||
hosts = [f"{ip_address_prefix}{suffix}" for suffix in ip_address_range]
|
hosts = [f"{ip_address_prefix}{suffix}" for suffix in ip_address_range]
|
||||||
|
|
||||||
ssh_port = 11022
|
ssh_port = 11022
|
||||||
@@ -459,34 +510,47 @@ def main():
|
|||||||
|
|
||||||
|
|
||||||
for host in hosts:
|
for host in hosts:
|
||||||
print(f"{host}", end=" - ", flush=True)
|
#print(f"{host}", end=" - ", flush=True)
|
||||||
|
|
||||||
hostname = ""
|
hostname = ""
|
||||||
result = ""
|
result = ""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
activate_ssh(host)
|
activate_ssh(host, True)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Exception: {e}")
|
print(f"Exception: {e}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
with Connection(host=host, user=ssh_user, port=ssh_port, connect_timeout=60, connect_kwargs=connect_args) as c:
|
with Connection(host=host, user=ssh_user, port=ssh_port, connect_timeout=60, connect_kwargs=connect_args) as c:
|
||||||
|
|
||||||
|
# try:
|
||||||
|
# print(f"Hostname:", end=" ", flush=True)
|
||||||
|
# result = execute_command(c, "hostname")
|
||||||
|
# print(f"{result.strip()}", end="\n", flush=True)
|
||||||
|
# hostname = str.lower(result)
|
||||||
|
# except Exception as e:
|
||||||
|
# print(f"[Hostname] Exception: {e}")
|
||||||
|
# continue
|
||||||
|
|
||||||
|
# print(f"cURL:", end=" ", flush=True)
|
||||||
|
# result = check_for_specific_curl_error(c)
|
||||||
|
# if result:
|
||||||
|
# print(f"✅", end="\n", flush=True)
|
||||||
|
# else:
|
||||||
|
# print(f"❌", end="\n", flush=True)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
print(f"Hostname:", end=" ", flush=True)
|
|
||||||
result = execute_command(c, "hostname")
|
result = execute_command(c, "hostname")
|
||||||
print(f"{result.strip()}", end="\n", flush=True)
|
print(f"{host};{result.strip()}", end=";", flush=True)
|
||||||
hostname = str.lower(result)
|
hostname = str.lower(result)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[Hostname] Exception: {e}")
|
print(f"{host};ERROR")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
print(f"cURL:", end=" ", flush=True)
|
result = read_remote_config_sudo(c, "/etc/cube/config-azure.properties", ssh_password)
|
||||||
result = execute_command(c, "curl -m 15 -x https://10.81.35.126:8080 https://iot-ingest-ess-prod.azure-devices.net")
|
print(cloud_configuration_csv(result))
|
||||||
print(f"{result.strip()}", end="\n", flush=True)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[cURL] Exception: {e}")
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# try:
|
# try:
|
||||||
@@ -508,6 +572,35 @@ def main():
|
|||||||
# continue
|
# continue
|
||||||
|
|
||||||
# cloud_configuration_check(hostname, result, "iot-ingest-ess-prod.azure-devices.net", "10.81.35.126", "8080")
|
# cloud_configuration_check(hostname, result, "iot-ingest-ess-prod.azure-devices.net", "10.81.35.126", "8080")
|
||||||
|
# result_telemetry_off = set_config_field(result, "telemetry-on", False)
|
||||||
|
# result = result_telemetry_off
|
||||||
|
# cloud_configuration_check(hostname, result, "iot-ingest-ess-prod.azure-devices.net", "10.81.35.126", "8080")
|
||||||
|
|
||||||
|
# try:
|
||||||
|
# write_remote_config_sudo(c, "/etc/cube/config-azure.properties", result, ssh_password, "cube", "root", "644")
|
||||||
|
# print(f"✅", end="\n", flush=True)
|
||||||
|
# except Exception as e:
|
||||||
|
# print(f"❌", end="\n", flush=True)
|
||||||
|
# print(f"[Proxy configuration] Exception: {e}")
|
||||||
|
# continue
|
||||||
|
|
||||||
|
# print(f"Checking Cloud configuration:", end=" ", flush=True)
|
||||||
|
# try:
|
||||||
|
# result = read_remote_config_sudo(c, "/etc/cube/config-azure.properties", ssh_password)
|
||||||
|
# print(f"✅", end="\n", flush=True)
|
||||||
|
# except Exception as e:
|
||||||
|
# print(f"❌", end="\n", flush=True)
|
||||||
|
# print(f"[Proxy verification] Exception: {e}")
|
||||||
|
# continue
|
||||||
|
|
||||||
|
# try:
|
||||||
|
# print(f"Restarting cube-web-cloudagent: ", end=" ", flush=True)
|
||||||
|
# execute_sudo_command(c, "systemctl restart cube-web-cloudagent", ssh_password)
|
||||||
|
# print(f"✅", end="\n", flush=True)
|
||||||
|
# except Exception as e:
|
||||||
|
# print(f"❌", end="\n", flush=True)
|
||||||
|
# print(f"[Restarting cube-web-cloudagent] Exception: {e}")
|
||||||
|
# continue
|
||||||
|
|
||||||
# print(f"Setting proxy configuration:", end="\n", flush=True)
|
# print(f"Setting proxy configuration:", end="\n", flush=True)
|
||||||
# result_proxy_host = set_config_field(result, "proxy-host", "10.81.35.126", True)
|
# result_proxy_host = set_config_field(result, "proxy-host", "10.81.35.126", True)
|
||||||
|
|||||||
Reference in New Issue
Block a user