Compare commits
4 Commits
acl-signal
...
otel-expor
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef53553e9d | ||
| d1e50db304 | |||
|
|
df0d7c2d84 | ||
| 16f4b4acad |
@@ -59,7 +59,7 @@ else
|
|||||||
new SectionDef("Equipment", "Equipment", "UNS level-5 rows + identification fields"),
|
new SectionDef("Equipment", "Equipment", "UNS level-5 rows + identification fields"),
|
||||||
new SectionDef("Tag", "Tags", "Per-device tag definitions + poll-group binding"),
|
new SectionDef("Tag", "Tags", "Per-device tag definitions + poll-group binding"),
|
||||||
new SectionDef("UnsLine", "UNS structure", "Site / Area / Line hierarchy (proc-extension pending)"),
|
new SectionDef("UnsLine", "UNS structure", "Site / Area / Line hierarchy (proc-extension pending)"),
|
||||||
new SectionDef("NodeAcl", "ACLs", "LDAP-group → node-scope permission grants (proc-extension pending)"),
|
new SectionDef("NodeAcl", "ACLs", "LDAP-group → node-scope permission grants (logical id = LdapGroup|ScopeKind|ScopeId)"),
|
||||||
};
|
};
|
||||||
|
|
||||||
private List<DiffRow>? _rows;
|
private List<DiffRow>? _rows;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using OpenTelemetry.Metrics;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using ZB.MOM.WW.OtOpcUa.Admin.Components;
|
using ZB.MOM.WW.OtOpcUa.Admin.Components;
|
||||||
using ZB.MOM.WW.OtOpcUa.Admin.Hubs;
|
using ZB.MOM.WW.OtOpcUa.Admin.Hubs;
|
||||||
@@ -70,6 +71,19 @@ builder.Services.AddScoped<ILdapAuthService, LdapAuthService>();
|
|||||||
// SignalR real-time fleet status + alerts (admin-ui.md §"Real-Time Updates").
|
// SignalR real-time fleet status + alerts (admin-ui.md §"Real-Time Updates").
|
||||||
builder.Services.AddHostedService<FleetStatusPoller>();
|
builder.Services.AddHostedService<FleetStatusPoller>();
|
||||||
|
|
||||||
|
// OpenTelemetry Prometheus exporter — Meter stream from RedundancyMetrics + any future
|
||||||
|
// Admin-side instrumentation lands on the /metrics endpoint Prometheus scrapes. Pull-based
|
||||||
|
// means no OTel Collector deployment required for the common deploy-in-a-K8s case; appsettings
|
||||||
|
// Metrics:Prometheus:Enabled=false disables the endpoint entirely for locked-down deployments.
|
||||||
|
var metricsEnabled = builder.Configuration.GetValue("Metrics:Prometheus:Enabled", true);
|
||||||
|
if (metricsEnabled)
|
||||||
|
{
|
||||||
|
builder.Services.AddOpenTelemetry()
|
||||||
|
.WithMetrics(m => m
|
||||||
|
.AddMeter(RedundancyMetrics.MeterName)
|
||||||
|
.AddPrometheusExporter());
|
||||||
|
}
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
app.UseSerilogRequestLogging();
|
app.UseSerilogRequestLogging();
|
||||||
@@ -87,6 +101,15 @@ app.MapPost("/auth/logout", async (HttpContext ctx) =>
|
|||||||
app.MapHub<FleetStatusHub>("/hubs/fleet");
|
app.MapHub<FleetStatusHub>("/hubs/fleet");
|
||||||
app.MapHub<AlertHub>("/hubs/alerts");
|
app.MapHub<AlertHub>("/hubs/alerts");
|
||||||
|
|
||||||
|
if (metricsEnabled)
|
||||||
|
{
|
||||||
|
// Prometheus scrape endpoint — expose instrumentation registered in the OTel MeterProvider
|
||||||
|
// above. Emits text-format metrics at /metrics; auth is intentionally NOT required (Prometheus
|
||||||
|
// scrape jobs typically run on a trusted network). Operators who need auth put the endpoint
|
||||||
|
// behind a reverse-proxy basic-auth gate per fleet-ops convention.
|
||||||
|
app.MapPrometheusScrapingEndpoint();
|
||||||
|
}
|
||||||
|
|
||||||
app.MapRazorComponents<App>().AddInteractiveServerRenderMode();
|
app.MapRazorComponents<App>().AddInteractiveServerRenderMode();
|
||||||
|
|
||||||
await app.RunAsync();
|
await app.RunAsync();
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
<PackageReference Include="Novell.Directory.Ldap.NETStandard" Version="3.6.0"/>
|
<PackageReference Include="Novell.Directory.Ldap.NETStandard" Version="3.6.0"/>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="10.0.0"/>
|
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="10.0.0"/>
|
||||||
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0"/>
|
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0"/>
|
||||||
|
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.15.2"/>
|
||||||
|
<PackageReference Include="OpenTelemetry.Exporter.Prometheus.AspNetCore" Version="1.15.2-beta.1"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -23,5 +23,10 @@
|
|||||||
},
|
},
|
||||||
"Serilog": {
|
"Serilog": {
|
||||||
"MinimumLevel": "Information"
|
"MinimumLevel": "Information"
|
||||||
|
},
|
||||||
|
"Metrics": {
|
||||||
|
"Prometheus": {
|
||||||
|
"Enabled": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,172 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace ZB.MOM.WW.OtOpcUa.Configuration.Migrations
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Extends <c>dbo.sp_ComputeGenerationDiff</c> to emit <c>NodeAcl</c> rows alongside the
|
||||||
|
/// existing Namespace/DriverInstance/Equipment/Tag output — closes the final slice of
|
||||||
|
/// task #196 (DiffViewer ACL section). Logical id for NodeAcl is a composite
|
||||||
|
/// <c>LdapGroup|ScopeKind|ScopeId</c> triple so a Change row surfaces whether the grant
|
||||||
|
/// shifted permissions, moved scope, or was added/removed outright.
|
||||||
|
/// </summary>
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class ExtendComputeGenerationDiffWithNodeAcl : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql(Procs.ComputeGenerationDiffV2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.Sql(Procs.ComputeGenerationDiffV1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Procs
|
||||||
|
{
|
||||||
|
/// <summary>V2 — adds the NodeAcl section to the diff output.</summary>
|
||||||
|
public const string ComputeGenerationDiffV2 = @"
|
||||||
|
CREATE OR ALTER PROCEDURE dbo.sp_ComputeGenerationDiff
|
||||||
|
@FromGenerationId bigint,
|
||||||
|
@ToGenerationId bigint
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON;
|
||||||
|
|
||||||
|
CREATE TABLE #diff (TableName nvarchar(32), LogicalId nvarchar(128), ChangeKind nvarchar(16));
|
||||||
|
|
||||||
|
WITH f AS (SELECT NamespaceId AS LogicalId, CHECKSUM(NamespaceUri, Kind, Enabled, Notes) AS Sig FROM dbo.Namespace WHERE GenerationId = @FromGenerationId),
|
||||||
|
t AS (SELECT NamespaceId AS LogicalId, CHECKSUM(NamespaceUri, Kind, Enabled, Notes) AS Sig FROM dbo.Namespace WHERE GenerationId = @ToGenerationId)
|
||||||
|
INSERT #diff
|
||||||
|
SELECT 'Namespace', CONVERT(nvarchar(128), COALESCE(f.LogicalId, t.LogicalId)),
|
||||||
|
CASE WHEN f.LogicalId IS NULL THEN 'Added'
|
||||||
|
WHEN t.LogicalId IS NULL THEN 'Removed'
|
||||||
|
WHEN f.Sig <> t.Sig THEN 'Modified'
|
||||||
|
ELSE 'Unchanged' END
|
||||||
|
FROM f FULL OUTER JOIN t ON f.LogicalId = t.LogicalId
|
||||||
|
WHERE f.LogicalId IS NULL OR t.LogicalId IS NULL OR f.Sig <> t.Sig;
|
||||||
|
|
||||||
|
WITH f AS (SELECT DriverInstanceId AS LogicalId, CHECKSUM(ClusterId, NamespaceId, Name, DriverType, Enabled, CONVERT(varchar(max), DriverConfig)) AS Sig FROM dbo.DriverInstance WHERE GenerationId = @FromGenerationId),
|
||||||
|
t AS (SELECT DriverInstanceId AS LogicalId, CHECKSUM(ClusterId, NamespaceId, Name, DriverType, Enabled, CONVERT(varchar(max), DriverConfig)) AS Sig FROM dbo.DriverInstance WHERE GenerationId = @ToGenerationId)
|
||||||
|
INSERT #diff
|
||||||
|
SELECT 'DriverInstance', CONVERT(nvarchar(128), COALESCE(f.LogicalId, t.LogicalId)),
|
||||||
|
CASE WHEN f.LogicalId IS NULL THEN 'Added'
|
||||||
|
WHEN t.LogicalId IS NULL THEN 'Removed'
|
||||||
|
WHEN f.Sig <> t.Sig THEN 'Modified'
|
||||||
|
ELSE 'Unchanged' END
|
||||||
|
FROM f FULL OUTER JOIN t ON f.LogicalId = t.LogicalId
|
||||||
|
WHERE f.LogicalId IS NULL OR t.LogicalId IS NULL OR f.Sig <> t.Sig;
|
||||||
|
|
||||||
|
WITH f AS (SELECT EquipmentId AS LogicalId, CHECKSUM(EquipmentUuid, DriverInstanceId, UnsLineId, Name, MachineCode, ZTag, SAPID, EquipmentClassRef, Manufacturer, Model, SerialNumber) AS Sig FROM dbo.Equipment WHERE GenerationId = @FromGenerationId),
|
||||||
|
t AS (SELECT EquipmentId AS LogicalId, CHECKSUM(EquipmentUuid, DriverInstanceId, UnsLineId, Name, MachineCode, ZTag, SAPID, EquipmentClassRef, Manufacturer, Model, SerialNumber) AS Sig FROM dbo.Equipment WHERE GenerationId = @ToGenerationId)
|
||||||
|
INSERT #diff
|
||||||
|
SELECT 'Equipment', CONVERT(nvarchar(128), COALESCE(f.LogicalId, t.LogicalId)),
|
||||||
|
CASE WHEN f.LogicalId IS NULL THEN 'Added'
|
||||||
|
WHEN t.LogicalId IS NULL THEN 'Removed'
|
||||||
|
WHEN f.Sig <> t.Sig THEN 'Modified'
|
||||||
|
ELSE 'Unchanged' END
|
||||||
|
FROM f FULL OUTER JOIN t ON f.LogicalId = t.LogicalId
|
||||||
|
WHERE f.LogicalId IS NULL OR t.LogicalId IS NULL OR f.Sig <> t.Sig;
|
||||||
|
|
||||||
|
WITH f AS (SELECT TagId AS LogicalId, CHECKSUM(DriverInstanceId, DeviceId, EquipmentId, PollGroupId, FolderPath, Name, DataType, AccessLevel, WriteIdempotent, CONVERT(varchar(max), TagConfig)) AS Sig FROM dbo.Tag WHERE GenerationId = @FromGenerationId),
|
||||||
|
t AS (SELECT TagId AS LogicalId, CHECKSUM(DriverInstanceId, DeviceId, EquipmentId, PollGroupId, FolderPath, Name, DataType, AccessLevel, WriteIdempotent, CONVERT(varchar(max), TagConfig)) AS Sig FROM dbo.Tag WHERE GenerationId = @ToGenerationId)
|
||||||
|
INSERT #diff
|
||||||
|
SELECT 'Tag', CONVERT(nvarchar(128), COALESCE(f.LogicalId, t.LogicalId)),
|
||||||
|
CASE WHEN f.LogicalId IS NULL THEN 'Added'
|
||||||
|
WHEN t.LogicalId IS NULL THEN 'Removed'
|
||||||
|
WHEN f.Sig <> t.Sig THEN 'Modified'
|
||||||
|
ELSE 'Unchanged' END
|
||||||
|
FROM f FULL OUTER JOIN t ON f.LogicalId = t.LogicalId
|
||||||
|
WHERE f.LogicalId IS NULL OR t.LogicalId IS NULL OR f.Sig <> t.Sig;
|
||||||
|
|
||||||
|
-- NodeAcl section. Logical id is the (LdapGroup, ScopeKind, ScopeId) triple so the diff
|
||||||
|
-- distinguishes same row with new permissions (Modified via CHECKSUM on PermissionFlags + Notes)
|
||||||
|
-- from a scope move (which surfaces as Added + Removed of different logical ids).
|
||||||
|
WITH f AS (
|
||||||
|
SELECT CONVERT(nvarchar(128), LdapGroup + '|' + CONVERT(nvarchar(16), ScopeKind) + '|' + ISNULL(ScopeId, '(cluster)')) AS LogicalId,
|
||||||
|
CHECKSUM(ClusterId, PermissionFlags, Notes) AS Sig
|
||||||
|
FROM dbo.NodeAcl WHERE GenerationId = @FromGenerationId),
|
||||||
|
t AS (
|
||||||
|
SELECT CONVERT(nvarchar(128), LdapGroup + '|' + CONVERT(nvarchar(16), ScopeKind) + '|' + ISNULL(ScopeId, '(cluster)')) AS LogicalId,
|
||||||
|
CHECKSUM(ClusterId, PermissionFlags, Notes) AS Sig
|
||||||
|
FROM dbo.NodeAcl WHERE GenerationId = @ToGenerationId)
|
||||||
|
INSERT #diff
|
||||||
|
SELECT 'NodeAcl', COALESCE(f.LogicalId, t.LogicalId),
|
||||||
|
CASE WHEN f.LogicalId IS NULL THEN 'Added'
|
||||||
|
WHEN t.LogicalId IS NULL THEN 'Removed'
|
||||||
|
WHEN f.Sig <> t.Sig THEN 'Modified'
|
||||||
|
ELSE 'Unchanged' END
|
||||||
|
FROM f FULL OUTER JOIN t ON f.LogicalId = t.LogicalId
|
||||||
|
WHERE f.LogicalId IS NULL OR t.LogicalId IS NULL OR f.Sig <> t.Sig;
|
||||||
|
|
||||||
|
SELECT TableName, LogicalId, ChangeKind FROM #diff;
|
||||||
|
DROP TABLE #diff;
|
||||||
|
END
|
||||||
|
";
|
||||||
|
|
||||||
|
/// <summary>V1 — exact proc shipped in migration 20260417215224_StoredProcedures. Restored on Down().</summary>
|
||||||
|
public const string ComputeGenerationDiffV1 = @"
|
||||||
|
CREATE OR ALTER PROCEDURE dbo.sp_ComputeGenerationDiff
|
||||||
|
@FromGenerationId bigint,
|
||||||
|
@ToGenerationId bigint
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON;
|
||||||
|
|
||||||
|
CREATE TABLE #diff (TableName nvarchar(32), LogicalId nvarchar(64), ChangeKind nvarchar(16));
|
||||||
|
|
||||||
|
WITH f AS (SELECT NamespaceId AS LogicalId, CHECKSUM(NamespaceUri, Kind, Enabled, Notes) AS Sig FROM dbo.Namespace WHERE GenerationId = @FromGenerationId),
|
||||||
|
t AS (SELECT NamespaceId AS LogicalId, CHECKSUM(NamespaceUri, Kind, Enabled, Notes) AS Sig FROM dbo.Namespace WHERE GenerationId = @ToGenerationId)
|
||||||
|
INSERT #diff
|
||||||
|
SELECT 'Namespace', CONVERT(nvarchar(64), COALESCE(f.LogicalId, t.LogicalId)),
|
||||||
|
CASE WHEN f.LogicalId IS NULL THEN 'Added'
|
||||||
|
WHEN t.LogicalId IS NULL THEN 'Removed'
|
||||||
|
WHEN f.Sig <> t.Sig THEN 'Modified'
|
||||||
|
ELSE 'Unchanged' END
|
||||||
|
FROM f FULL OUTER JOIN t ON f.LogicalId = t.LogicalId
|
||||||
|
WHERE f.LogicalId IS NULL OR t.LogicalId IS NULL OR f.Sig <> t.Sig;
|
||||||
|
|
||||||
|
WITH f AS (SELECT DriverInstanceId AS LogicalId, CHECKSUM(ClusterId, NamespaceId, Name, DriverType, Enabled, CONVERT(varchar(max), DriverConfig)) AS Sig FROM dbo.DriverInstance WHERE GenerationId = @FromGenerationId),
|
||||||
|
t AS (SELECT DriverInstanceId AS LogicalId, CHECKSUM(ClusterId, NamespaceId, Name, DriverType, Enabled, CONVERT(varchar(max), DriverConfig)) AS Sig FROM dbo.DriverInstance WHERE GenerationId = @ToGenerationId)
|
||||||
|
INSERT #diff
|
||||||
|
SELECT 'DriverInstance', CONVERT(nvarchar(64), COALESCE(f.LogicalId, t.LogicalId)),
|
||||||
|
CASE WHEN f.LogicalId IS NULL THEN 'Added'
|
||||||
|
WHEN t.LogicalId IS NULL THEN 'Removed'
|
||||||
|
WHEN f.Sig <> t.Sig THEN 'Modified'
|
||||||
|
ELSE 'Unchanged' END
|
||||||
|
FROM f FULL OUTER JOIN t ON f.LogicalId = t.LogicalId
|
||||||
|
WHERE f.LogicalId IS NULL OR t.LogicalId IS NULL OR f.Sig <> t.Sig;
|
||||||
|
|
||||||
|
WITH f AS (SELECT EquipmentId AS LogicalId, CHECKSUM(EquipmentUuid, DriverInstanceId, UnsLineId, Name, MachineCode, ZTag, SAPID, EquipmentClassRef, Manufacturer, Model, SerialNumber) AS Sig FROM dbo.Equipment WHERE GenerationId = @FromGenerationId),
|
||||||
|
t AS (SELECT EquipmentId AS LogicalId, CHECKSUM(EquipmentUuid, DriverInstanceId, UnsLineId, Name, MachineCode, ZTag, SAPID, EquipmentClassRef, Manufacturer, Model, SerialNumber) AS Sig FROM dbo.Equipment WHERE GenerationId = @ToGenerationId)
|
||||||
|
INSERT #diff
|
||||||
|
SELECT 'Equipment', CONVERT(nvarchar(64), COALESCE(f.LogicalId, t.LogicalId)),
|
||||||
|
CASE WHEN f.LogicalId IS NULL THEN 'Added'
|
||||||
|
WHEN t.LogicalId IS NULL THEN 'Removed'
|
||||||
|
WHEN f.Sig <> t.Sig THEN 'Modified'
|
||||||
|
ELSE 'Unchanged' END
|
||||||
|
FROM f FULL OUTER JOIN t ON f.LogicalId = t.LogicalId
|
||||||
|
WHERE f.LogicalId IS NULL OR t.LogicalId IS NULL OR f.Sig <> t.Sig;
|
||||||
|
|
||||||
|
WITH f AS (SELECT TagId AS LogicalId, CHECKSUM(DriverInstanceId, DeviceId, EquipmentId, PollGroupId, FolderPath, Name, DataType, AccessLevel, WriteIdempotent, CONVERT(varchar(max), TagConfig)) AS Sig FROM dbo.Tag WHERE GenerationId = @FromGenerationId),
|
||||||
|
t AS (SELECT TagId AS LogicalId, CHECKSUM(DriverInstanceId, DeviceId, EquipmentId, PollGroupId, FolderPath, Name, DataType, AccessLevel, WriteIdempotent, CONVERT(varchar(max), TagConfig)) AS Sig FROM dbo.Tag WHERE GenerationId = @ToGenerationId)
|
||||||
|
INSERT #diff
|
||||||
|
SELECT 'Tag', CONVERT(nvarchar(64), COALESCE(f.LogicalId, t.LogicalId)),
|
||||||
|
CASE WHEN f.LogicalId IS NULL THEN 'Added'
|
||||||
|
WHEN t.LogicalId IS NULL THEN 'Removed'
|
||||||
|
WHEN f.Sig <> t.Sig THEN 'Modified'
|
||||||
|
ELSE 'Unchanged' END
|
||||||
|
FROM f FULL OUTER JOIN t ON f.LogicalId = t.LogicalId
|
||||||
|
WHERE f.LogicalId IS NULL OR t.LogicalId IS NULL OR f.Sig <> t.Sig;
|
||||||
|
|
||||||
|
SELECT TableName, LogicalId, ChangeKind FROM #diff;
|
||||||
|
DROP TABLE #diff;
|
||||||
|
END
|
||||||
|
";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user