Compare commits
12 Commits
7dc7d6abd8
...
fcc8c811cc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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
|
||||
dist/*
|
||||
*.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')
|
||||
|
||||
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_'
|
||||
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]) + ".")
|
||||
for index, row in filtered_df_prop.iterrows():
|
||||
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:
|
||||
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'])]
|
||||
@@ -104,10 +104,11 @@ def excel_parser(sheet_det, slave_no, slave_device, allowed_name_characters, dsh
|
||||
else:
|
||||
logging.debug("Poll interval undefined, using 1000ms as default.")
|
||||
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'])
|
||||
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["dataType"] = row['type']
|
||||
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["properties"] = [{"key": "deviceType", "value": "AC_GATEWAY"}, {"key": "cdid", "value": current_device}]
|
||||
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["samplingMode"] = "allValues"
|
||||
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 \"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)"
|
||||
|
||||
def bitfield_jq_filter(current_device):
|
||||
|
||||
@@ -15,20 +15,21 @@ if CONNECTION_STRING == "":
|
||||
print("Provide a connection string for the Iot Hub before running the script!")
|
||||
exit(13)
|
||||
|
||||
SITE_NAME = "LIBERTY"
|
||||
SITE_NAME = "Bell"
|
||||
|
||||
|
||||
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)
|
||||
|
||||
devices = []
|
||||
for item in query_result.items:
|
||||
deviceId = str(item.device_id)
|
||||
site = str(item.tags['site'])
|
||||
number = int(item.tags['number'])
|
||||
cloud_version = str(item.tags['version'])
|
||||
site = str(item.tags.get('site')) if item.tags.get('site') else None
|
||||
number = int(item.tags.get('number')) if item.tags.get('number') else None
|
||||
cloud_version = str(item.tags.get('version')) if item.tags.get('version') else None
|
||||
devices.append([deviceId, site, number, cloud_version])
|
||||
|
||||
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,
|
||||
"number": twin.tags.get("number") 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,
|
||||
"last_activity_time": twin.last_activity_time,
|
||||
"last_activity_time": twin.last_activity_time
|
||||
})
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
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_20260204.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:
|
||||
raise
|
||||
|
||||
def activate_ssh(ip_address):
|
||||
def activate_ssh(ip_address, silent = False):
|
||||
|
||||
# Ensure the URL uses HTTPS
|
||||
url = ip_address
|
||||
@@ -104,17 +104,23 @@ def activate_ssh(ip_address):
|
||||
if not verify_ssl:
|
||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||
|
||||
print(f"HTTPS", end=" ", flush=True)
|
||||
if not silent:
|
||||
print(f"HTTPS", end=" ", flush=True)
|
||||
try:
|
||||
token = authenticate(url)
|
||||
print(f"✅", end="", flush=True)
|
||||
if not silent:
|
||||
print(f"✅", end="", flush=True)
|
||||
except Exception as e:
|
||||
print(f"❌", flush=True)
|
||||
if not silent:
|
||||
print(f"❌", flush=True)
|
||||
raise
|
||||
print(f"SSH", end=" ", flush=True)
|
||||
if not silent:
|
||||
print(f"SSH", end=" ", flush=True)
|
||||
try:
|
||||
set_ssh_status(url, token)
|
||||
print(f"✅", end="\n", flush=True)
|
||||
if not silent:
|
||||
print(f"✅", end="\n", flush=True)
|
||||
except Exception as e:
|
||||
print(f"❌", flush=True)
|
||||
if not silent:
|
||||
print(f"❌", flush=True)
|
||||
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)
|
||||
|
||||
ip_address_prefix = "10.81.60."
|
||||
ip_address_prefix = "10.81.56."
|
||||
ssh_command = "hostname"
|
||||
|
||||
csv_filename = "Grandpuits_01.csv"
|
||||
SITE_NAME = "Grandpuits"
|
||||
csv_filename = "DK2_01.csv"
|
||||
SITE_NAME = "DK2"
|
||||
|
||||
ssh_username = os.getenv("DEFAULT_CUBE_LINUX_ADMIN_USER")
|
||||
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()
|
||||
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 os
|
||||
from dotenv import load_dotenv
|
||||
import time
|
||||
|
||||
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"
|
||||
response = requests.post(patch_url, json=payload, headers=headers, verify=False)
|
||||
json_data = json.loads(response.content.decode())
|
||||
#print(json.dumps(json_data, indent=4, sort_keys=True))
|
||||
id = json_data['data']['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("CREATED ON: " + str(created))
|
||||
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:
|
||||
print(f"Failed to send GET request to device {device_ip}. Status code: {response.status_code}")
|
||||
print(response.content)
|
||||
@@ -281,28 +315,47 @@ 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(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 = {
|
||||
"acceptEULA": True,
|
||||
"name": default_user,
|
||||
"password": default_password
|
||||
}
|
||||
|
||||
device_ip_address = str("10.84.171." + str(i))
|
||||
|
||||
#print(device_ip_address, end="")
|
||||
token = authenticate(device_ip_address, payload_auth)
|
||||
print(hash(token))
|
||||
|
||||
upgrade_url = "http://10.84.157.137:8080/1.6.0.yaml"
|
||||
|
||||
if token:
|
||||
#id = send_upgrade_request(device_ip_address,token,upgrade_url)
|
||||
#print(id)
|
||||
#get_upgrade_job(device_ip_address, token, 6)
|
||||
#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)
|
||||
#send_upgrade_request(device_ip_address, token, upgrade_url)
|
||||
id = get_last_job(device_ip_address, token)
|
||||
start_upgrade_job(device_ip_address, token, id)
|
||||
time.sleep(300)
|
||||
#input("Continue?")
|
||||
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 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):
|
||||
return {
|
||||
'description': '',
|
||||
@@ -112,7 +134,6 @@ for device in devices:
|
||||
current_device_modules = registry_manager.get_modules(device.deviceId)
|
||||
for module in current_device_modules:
|
||||
if (module.module_id == module_id):
|
||||
#print(f"{module.module_id}")
|
||||
device.setModule(module)
|
||||
payload = '{"method":"GET", "path":"/azure-iotedge/messages"}'
|
||||
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)
|
||||
device_messages = json.dumps(response.payload, indent = 2)
|
||||
parsed_messages = json.loads(device_messages)
|
||||
#print("Parsed messages")
|
||||
message_ids_with_tags = {}
|
||||
tags_to_on_event = defaultdict(set)
|
||||
for message in parsed_messages["data"]:
|
||||
@@ -131,6 +151,7 @@ for device in devices:
|
||||
|
||||
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():
|
||||
if "InvSt" in tag_list and "OE" in topic_name:
|
||||
message_ids_with_tags.setdefault(message_id, message)
|
||||
@@ -140,68 +161,69 @@ for device in devices:
|
||||
message_ids_with_tags.setdefault(message_id, message)
|
||||
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()}")
|
||||
# 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
|
||||
# twelve_exists = False
|
||||
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}")
|
||||
|
||||
# for message in message_ids_with_tags.values():
|
||||
# if message['outputTopic'] == "REL_OE_PCS":
|
||||
# oe_exists = True
|
||||
# if message['outputTopic'] == "REL_OE_PCS_12H":
|
||||
# twelve_exists = True
|
||||
oe_exists = False
|
||||
twelve_exists = False
|
||||
|
||||
# if (oe_exists == False or twelve_exists == False):
|
||||
# first_message = next(iter(message_ids_with_tags.values()))
|
||||
# properties = first_message['properties']
|
||||
# cdid = next((item['value'] for item in properties if item['key'] == 'cdid'))
|
||||
# print(f"{cdid}")
|
||||
for message in message_ids_with_tags.values():
|
||||
if message['outputTopic'] == "REL_OE_PCS":
|
||||
oe_exists = True
|
||||
if message['outputTopic'] == "REL_OE_PCS_12H":
|
||||
twelve_exists = True
|
||||
|
||||
# modbus_tags = {
|
||||
# asset: sorted(list(tags))
|
||||
# for asset, tags in tags_to_on_event.items()
|
||||
# }
|
||||
# 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))
|
||||
if (oe_exists == False or twelve_exists == False):
|
||||
first_message = next(iter(message_ids_with_tags.values()))
|
||||
properties = first_message['properties']
|
||||
cdid = next((item['value'] for item in properties if item['key'] == 'cdid'))
|
||||
print(f"{cdid}")
|
||||
|
||||
# if (twelve_exists == False):
|
||||
# new_payload = '{"method":"POST", "path":"/azure-iotedge/messages", "requestBody":' + json.dumps(generate_12h(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))
|
||||
modbus_tags = {
|
||||
asset: sorted(list(tags))
|
||||
for asset, tags in tags_to_on_event.items()
|
||||
}
|
||||
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 "OE" in message['outputTopic']:
|
||||
# continue
|
||||
# tags_to_remove = {"InvSt", "Fault_Id"}
|
||||
# for protocol, versions in message['tags'].items():
|
||||
# for version, tag_list in versions.items():
|
||||
# message['tags'][protocol][version] = [
|
||||
# tag for tag in tag_list if tag not in tags_to_remove
|
||||
# ]
|
||||
# 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'])
|
||||
if (twelve_exists == False):
|
||||
new_payload = '{"method":"POST", "path":"/azure-iotedge/messages", "requestBody":' + json.dumps(generate_12h(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 "OE" in message['outputTopic']:
|
||||
continue
|
||||
tags_to_remove = {"InvSt", "Fault_Id"}
|
||||
for protocol, versions in message['tags'].items():
|
||||
for version, tag_list in versions.items():
|
||||
message['tags'][protocol][version] = [
|
||||
tag for tag in tag_list if tag not in tags_to_remove
|
||||
]
|
||||
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")
|
||||
# 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("\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))
|
||||
except Exception as 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
|
||||
netmiko
|
||||
pexpect
|
||||
tqdm
|
||||
@@ -175,6 +175,14 @@ def find_config_value(config_content, option):
|
||||
# If the loop finishes without finding the option, 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):
|
||||
print(f"\tLight telemetry:", end=" ", flush=True)
|
||||
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)
|
||||
status = find_config_value(result, "telemetry-on")
|
||||
if status == "true":
|
||||
if status == "False":
|
||||
print(f"✅", end="\n", flush=True)
|
||||
else:
|
||||
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.
|
||||
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():
|
||||
"""Main function to parse arguments and orchestrate tasks."""
|
||||
ip_address_prefix = "10.81.56." # DK2 subnet
|
||||
ip_address_range = list(range(129, 145)) # From 129 to 144 (16 CUBEs)
|
||||
# ip_address_range.append(72) # Add 85 after 74.
|
||||
ip_address_prefix = "10.84.165." # DK2 subnet
|
||||
ip_address_range = list(range(131, 188)) # From 129 to 144 (16 CUBEs)
|
||||
# ip_address_range.append(85) # Add 85 after 74.
|
||||
hosts = [f"{ip_address_prefix}{suffix}" for suffix in ip_address_range]
|
||||
|
||||
ssh_port = 11022
|
||||
@@ -459,34 +510,47 @@ def main():
|
||||
|
||||
|
||||
for host in hosts:
|
||||
print(f"{host}", end=" - ", flush=True)
|
||||
#print(f"{host}", end=" - ", flush=True)
|
||||
|
||||
hostname = ""
|
||||
result = ""
|
||||
|
||||
try:
|
||||
activate_ssh(host)
|
||||
activate_ssh(host, True)
|
||||
except Exception as e:
|
||||
print(f"Exception: {e}")
|
||||
continue
|
||||
|
||||
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:
|
||||
print(f"Hostname:", end=" ", flush=True)
|
||||
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)
|
||||
except Exception as e:
|
||||
print(f"[Hostname] Exception: {e}")
|
||||
print(f"{host};ERROR")
|
||||
continue
|
||||
|
||||
try:
|
||||
print(f"cURL:", end=" ", flush=True)
|
||||
result = execute_command(c, "curl -m 15 -x https://10.81.35.126:8080 https://iot-ingest-ess-prod.azure-devices.net")
|
||||
print(f"{result.strip()}", end="\n", flush=True)
|
||||
result = read_remote_config_sudo(c, "/etc/cube/config-azure.properties", ssh_password)
|
||||
print(cloud_configuration_csv(result))
|
||||
except Exception as e:
|
||||
print(f"[cURL] Exception: {e}")
|
||||
continue
|
||||
|
||||
# try:
|
||||
@@ -508,6 +572,35 @@ def main():
|
||||
# continue
|
||||
|
||||
# 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)
|
||||
# result_proxy_host = set_config_field(result, "proxy-host", "10.81.35.126", True)
|
||||
|
||||
Reference in New Issue
Block a user