From 0c99d3566fe5532d6d1e80fbdc6493ab781de959 Mon Sep 17 00:00:00 2001 From: XtremeOwnageDotCom <5262735+XtremeOwnageDotCom@users.noreply.github.com> Date: Wed, 28 Aug 2024 21:38:35 -0500 Subject: [PATCH 1/6] Added a few minor improvements to logging, to better identify which broker we are trying to connect to. As well- indicate if the /config path exists. --- rPDU2MQTT/Program.cs | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/rPDU2MQTT/Program.cs b/rPDU2MQTT/Program.cs index eced263..27e968f 100644 --- a/rPDU2MQTT/Program.cs +++ b/rPDU2MQTT/Program.cs @@ -10,12 +10,28 @@ var host = Host.CreateDefaultBuilder(args) .ConfigureAppConfiguration((context, config) => { - config.SetBasePath(Directory.GetCurrentDirectory()) - .AddJsonFile("appsettings.Development.json", optional: true, reloadOnChange: true) + if (Directory.Exists("/config")) + { + Console.WriteLine($"Found /config directory"); + Console.WriteLine($"/config.appsettings.json exists: {File.Exists("/config/appsettings.json")}"); + // Check /Config directory. - .SetBasePath("/config") - .AddJsonFile("appsettings.Development.json", optional: true, reloadOnChange: true) - .AddEnvironmentVariables(); + config + .SetBasePath("/config") + .AddJsonFile("appsettings.Development.json", optional: true, reloadOnChange: true); + } + else + { + Console.WriteLine($"Did not find /config directory. Using {Directory.GetCurrentDirectory()}"); + Console.WriteLine($"appsettings.json exists: {File.Exists("appsettings.json")}"); + //Check for configuration files in the current directory. + config.SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.Development.json", optional: true, reloadOnChange: true); + } + + + config.AddEnvironmentVariables(); + }) .ConfigureServices((context, services) => { @@ -73,9 +89,12 @@ }) .Build(); - //Ensure we can actually connect to MQTT. var client = host.Services.GetRequiredService(); +var logger = host.Services.GetRequiredService>(); + +logger.LogInformation($"Connecting to MQTT Broker at {client.Options.Host}:{client.Options.Port}"); + await client.ConnectAsync(); host.Run(); \ No newline at end of file From 210ec9de4a7acc468bb3d00702472b0ac7c0e287 Mon Sep 17 00:00:00 2001 From: XtremeOwnageDotCom <5262735+XtremeOwnageDotCom@users.noreply.github.com> Date: Wed, 28 Aug 2024 21:43:38 -0500 Subject: [PATCH 2/6] Log after we connect --- rPDU2MQTT/Program.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rPDU2MQTT/Program.cs b/rPDU2MQTT/Program.cs index 27e968f..3c060e1 100644 --- a/rPDU2MQTT/Program.cs +++ b/rPDU2MQTT/Program.cs @@ -97,4 +97,6 @@ await client.ConnectAsync(); +logger.LogInformation("Successfully connected to broker!"); + host.Run(); \ No newline at end of file From b01c30c8d3805735d426ac02b44d304abe0d2add Mon Sep 17 00:00:00 2001 From: XtremeOwnageDotCom <5262735+XtremeOwnageDotCom@users.noreply.github.com> Date: Wed, 28 Aug 2024 21:53:58 -0500 Subject: [PATCH 3/6] Added adjustable timeout to PDU. Defaulted to 2 seconds. --- rPDU2MQTT/Classes/PDU.cs | 2 ++ rPDU2MQTT/Models/Config/PduConfig.cs | 4 ++++ rPDU2MQTT/appsettings.json | 4 +++- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/rPDU2MQTT/Classes/PDU.cs b/rPDU2MQTT/Classes/PDU.cs index 9e289c0..03bbaf4 100644 --- a/rPDU2MQTT/Classes/PDU.cs +++ b/rPDU2MQTT/Classes/PDU.cs @@ -28,7 +28,9 @@ public PDU(Config config, HttpClient http, ILogger log) /// public async Task GetRootData_Public(CancellationToken cancellationToken) { + log.LogDebug("Querying /api"); var model = await http.GetFromJsonAsync>("/api", options: Models.PDU.Converter.Settings, cancellationToken); + log.LogDebug($"Query response {model.RetCode}"); //Process device data. processData(model.Data); diff --git a/rPDU2MQTT/Models/Config/PduConfig.cs b/rPDU2MQTT/Models/Config/PduConfig.cs index d69a4e0..cdc8586 100644 --- a/rPDU2MQTT/Models/Config/PduConfig.cs +++ b/rPDU2MQTT/Models/Config/PduConfig.cs @@ -27,4 +27,8 @@ public class PduConfig [Range(1, int.MaxValue, ErrorMessage = "PollInterval must be greater than 0.")] [Display(Description = "The polling interval for the PDU in seconds.")] public int PollInterval { get; set; } = 5; + + [Range(1, 60 * 10, ErrorMessage = "Expected timeout between 1 second, and 10 minutes.")] + [Display(Description = "Http timeout for requests to PDU")] + public int Timeout { get; set; } = 5; } \ No newline at end of file diff --git a/rPDU2MQTT/appsettings.json b/rPDU2MQTT/appsettings.json index ada8c67..cc4e8dd 100644 --- a/rPDU2MQTT/appsettings.json +++ b/rPDU2MQTT/appsettings.json @@ -22,7 +22,9 @@ "DeviceId": "A0AE260C851900C3", // Set to the URL, or IP for your PDU. "Url": "http://your-pdu-ip-or-dns/", - "PollInterval": 5 + "PollInterval": 5, + // Timeout for requests to/from the PDU, in seconds. + "Timeout": 5 }, // This section allows overriding the generated entity_id, and names for various objects. // Note- ID / entity_id, will only be set when the entity is created. From 69bbc0aab50926fcec797dee4de97158a3167842 Mon Sep 17 00:00:00 2001 From: XtremeOwnageDotCom <5262735+XtremeOwnageDotCom@users.noreply.github.com> Date: Wed, 28 Aug 2024 21:59:15 -0500 Subject: [PATCH 4/6] Added VERY basic manifests for kubernetes deployments. --- Examples/Kubernetes/configuration.yaml | 145 +++++++++++++++++++++++++ Examples/Kubernetes/deployment.yaml | 49 +++++++++ Examples/Kubernetes/exec | 3 + Examples/Kubernetes/namespace.yaml | 7 ++ 4 files changed, 204 insertions(+) create mode 100644 Examples/Kubernetes/configuration.yaml create mode 100644 Examples/Kubernetes/deployment.yaml create mode 100644 Examples/Kubernetes/exec create mode 100644 Examples/Kubernetes/namespace.yaml diff --git a/Examples/Kubernetes/configuration.yaml b/Examples/Kubernetes/configuration.yaml new file mode 100644 index 0000000..52325ce --- /dev/null +++ b/Examples/Kubernetes/configuration.yaml @@ -0,0 +1,145 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: configuration + namespace: rpdu2mqtt +data: + appsettings.json: | + { + "Logging": { + "LogLevel": { + "Default": "Information", + "System.Net.Http.HttpClient": "Warning", + "rPDU2MQTT.*": "Debug" + } + }, + "Mqtt": { + // Add your MQTT Username and Password here. + "Username": "user", + "Password": "password", + + // This will be the parent topic in MQTT, where keys are published to. + "ParentTopic": "Rack_PDU", + + // This is the client-ID used. + "ClientID": "rpdu2mqtt", + + // Set this to the host / ip of your MQTT server. + "Host": "localhost", + "Port": 1883, + "KeepAlive": 60 + }, + "Pdu": { + // Add your PDU's device ID here. + "DeviceId": "A0AE260C851900C3", + + // Set to the URL, or IP for your PDU. + "Url": "http://your-pdu-ip-or-dns/", + + // This is how often sensors will be published to MQTT (in seconds) + "PollInterval": 5, + + // Timeout for requests to/from the PDU, in seconds. + "Timeout": 5 + }, + // This section allows overriding the generated entity_id, and names for various objects. + // Note- ID / entity_id, will only be set when the entity is created. + // Do not touch or change it throug here after it has been created! Otherwise, you can end up with duplicates, or other issues. + // "Name" can be freely updated, and home assistant will reflect updated names instantly (after the discovery runs) + "Overrides": { + // Allows overriding the generated identifier for the PDU. Leave blank to disable. + // Warning- Do not change after creation! This is the root, unique identifier used, and should not change! + // IF, you change this, you will have a lot of duplicated devices, and entities!!!!!! + "PduID": null, + + // Allows overriding the generated entity name for the PDU. Leave blank to disable. + // This- can be changed at any time, and will update the name of the Device which represents the PDU. + // If null, this will default to the PDU's configured label. + "PduName": "Rack-PDU-1" + }, + "Outlets": { + // The "entity_id" can be overridden, and customized for each outlet. + // By default, if not specified, an entity ID will be automatically generated from the labels configured via the PDU. + // Note- don't change this after initial discovery. Will cause problems. + "ID": { + //"1": "kube02", + //"2": "dell_md1220", + //"3": "outlet_3" + //.. + }, + // The "name" can be overridden, and customized for each outlet. + // This, represents the "display name", which is the human-readable, pretty version. + // By default, if not specified, this will be automatically generated from the labels configured via the PDU. + "Name": { + //"1": "Proxmox: Kube02", + //"2": "Dell: MD1220", + //"3": "Outlet 3 Friendly Name" + //.. + }, + + ///Allows enabling, or disabling specific devices. + // Disabled devices will not be discovered or published to MQTT. + "Enabled": { + "1": true, + "2": true, + "3": true + } + }, + "Measurements": { + // This allows customizing the entity IDs generated for metrics. + // All measurement entity_ids will start with the generated ID for the device they belong to. + // ie, {device_name}_power, device_energy + // Output Format will be {EntityID}_{MeasurementID} + // Both measurement ID, and Name, is based on the data type presented from the PDU. + // This will affect ALL devices and entities, using measurements. + // Note- changing the ID only customizes the suffix. + "ID": { + "apparentpower": null, + "realpower": "power", + "energy": null, + "powerFactor": null, + "current": null, + "voltage": null + }, + + //Customize the names for measurements. + //Output Format will be {Entity Name} {Measurement Name} + "Name": { + "apparentpower": "Apparent Power", + "realpower": "Power", + "energy": "Energy", + "powerFactor": "Power Factor", + "current": "Current", + "voltage": "Voltage" + }, + + //Allow enabling, or disabling publishing specific measurement types. + //This applies to all entity types. + "Enabled": { + "apparentpower": true, + "realpower": true, + "energy": true, + "powerFactor": true, + "current": true, + "voltage": true, + + // These are not enabled- because they don't quite map to anything useful. + "balance": false, + "currentCrestFactor": false + } + }, + + // Ignore this section for now... + "Actions": { + "Enabled": false, + "Username": "actionsUser", + "Password": "actionsPass" + }, + "HomeAssistant": { + "DiscoveryEnabled": true, + "DiscoveryTopic": "homeassistant/discovery", + "DiscoveryInterval": 300, + // Default expireAfter interval applied to all sensors. After this time- the sensor will be marked as unavailable. + "SensorExpireAfterSeconds": 300 + } + } diff --git a/Examples/Kubernetes/deployment.yaml b/Examples/Kubernetes/deployment.yaml new file mode 100644 index 0000000..e1b8eba --- /dev/null +++ b/Examples/Kubernetes/deployment.yaml @@ -0,0 +1,49 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + role: app + name: rpdu2mqtt + namespace: rpdu2mqtt +spec: + replicas: 1 + revisionHistoryLimit: 1 + selector: + matchLabels: + app: rpdu2mqtt + role: app + template: + metadata: + labels: + app: rpdu2mqtt + role: app + spec: + dnsPolicy: ClusterFirst + containers: + - image: ghcr.io/xtremeownage/rpdu2mqtt:dev-name + name: app + securityContext: + runAsNonRoot: true + allowPrivilegeEscalation: false + runAsUser: 1654 + seccompProfile: + type: RuntimeDefault + capabilities: + drop: + - ALL + volumeMounts: + - name: config-files + mountPath: /config + resources: + limits: + memory: 800Mi + cpu: 300m + requests: + memory: 100Mi + cpu: 100m + volumes: + - name: config-files + configMap: + name: configuration + defaultMode: 0777 + \ No newline at end of file diff --git a/Examples/Kubernetes/exec b/Examples/Kubernetes/exec new file mode 100644 index 0000000..cbbf48c --- /dev/null +++ b/Examples/Kubernetes/exec @@ -0,0 +1,3 @@ +ns="rpdu2mqtt" +kubectl exec -it -n $ns $(kubectl get pod -n $ns -l role=app -o jsonpath='{.items[0].metadata.name}') -- $1 + diff --git a/Examples/Kubernetes/namespace.yaml b/Examples/Kubernetes/namespace.yaml new file mode 100644 index 0000000..ba228d7 --- /dev/null +++ b/Examples/Kubernetes/namespace.yaml @@ -0,0 +1,7 @@ +kind: Namespace +apiVersion: v1 +metadata: + name: rpdu2mqtt + labels: + name: rPDU2MQTT + backup-policy: daily-backup From 323f605be59e15c51a5ae05faa798d65228f63b8 Mon Sep 17 00:00:00 2001 From: XtremeOwnageDotCom <5262735+XtremeOwnageDotCom@users.noreply.github.com> Date: Wed, 28 Aug 2024 21:59:24 -0500 Subject: [PATCH 5/6] Added a bit more whitespace. --- rPDU2MQTT/appsettings.json | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/rPDU2MQTT/appsettings.json b/rPDU2MQTT/appsettings.json index cc4e8dd..6c80876 100644 --- a/rPDU2MQTT/appsettings.json +++ b/rPDU2MQTT/appsettings.json @@ -7,12 +7,17 @@ } }, "Mqtt": { + // Add your MQTT Username and Password here. "Username": "user", "Password": "password", + // This will be the parent topic in MQTT, where keys are published to. "ParentTopic": "Rack_PDU", + // This is the client-ID used. "ClientID": "rpdu2mqtt", + + // Set this to the host / ip of your MQTT server. "Host": "localhost", "Port": 1883, "KeepAlive": 60 @@ -20,9 +25,13 @@ "Pdu": { // Add your PDU's device ID here. "DeviceId": "A0AE260C851900C3", + // Set to the URL, or IP for your PDU. "Url": "http://your-pdu-ip-or-dns/", + + // This is how often sensors will be published to MQTT (in seconds) "PollInterval": 5, + // Timeout for requests to/from the PDU, in seconds. "Timeout": 5 }, @@ -112,6 +121,8 @@ "currentCrestFactor": false } }, + + // Ignore this section for now... "Actions": { "Enabled": false, "Username": "actionsUser", From 295642473933fffe905ff9a5ab91d0a26eadb3d3 Mon Sep 17 00:00:00 2001 From: XtremeOwnageDotCom <5262735+XtremeOwnageDotCom@users.noreply.github.com> Date: Wed, 28 Aug 2024 21:59:35 -0500 Subject: [PATCH 6/6] Added adjustable timeout. --- rPDU2MQTT/Program.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/rPDU2MQTT/Program.cs b/rPDU2MQTT/Program.cs index 3c060e1..32ea38b 100644 --- a/rPDU2MQTT/Program.cs +++ b/rPDU2MQTT/Program.cs @@ -64,6 +64,7 @@ services.AddHttpClient("pdu", client => { client.BaseAddress = new Uri(pduConfiguration.Url); + client.Timeout = TimeSpan.FromSeconds(pduConfiguration.Timeout); }); //Configure Services