@oneuptime/common 11.0.0 → 11.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (357) hide show
  1. package/Models/DatabaseModels/Alert.ts +110 -0
  2. package/Models/DatabaseModels/CephCluster.ts +964 -0
  3. package/Models/DatabaseModels/CephClusterLabelRule.ts +514 -0
  4. package/Models/DatabaseModels/CephClusterOwnerRule.ts +596 -0
  5. package/Models/DatabaseModels/CephClusterOwnerTeam.ts +487 -0
  6. package/Models/DatabaseModels/CephClusterOwnerUser.ts +486 -0
  7. package/Models/DatabaseModels/CephResource.ts +809 -0
  8. package/Models/DatabaseModels/Host.ts +64 -0
  9. package/Models/DatabaseModels/Incident.ts +110 -0
  10. package/Models/DatabaseModels/Index.ts +24 -0
  11. package/Models/DatabaseModels/ProxmoxCluster.ts +943 -0
  12. package/Models/DatabaseModels/ProxmoxClusterLabelRule.ts +514 -0
  13. package/Models/DatabaseModels/ProxmoxClusterOwnerRule.ts +596 -0
  14. package/Models/DatabaseModels/ProxmoxClusterOwnerTeam.ts +487 -0
  15. package/Models/DatabaseModels/ProxmoxClusterOwnerUser.ts +486 -0
  16. package/Models/DatabaseModels/ProxmoxResource.ts +726 -0
  17. package/Models/DatabaseModels/ScheduledMaintenance.ts +110 -0
  18. package/Server/API/BillingInvoiceAPI.ts +47 -7
  19. package/Server/API/CephResourceAPI.ts +134 -0
  20. package/Server/API/DashboardAPI.ts +46 -0
  21. package/Server/API/ProjectAPI.ts +15 -0
  22. package/Server/API/ProxmoxResourceAPI.ts +132 -0
  23. package/Server/API/ResellerPlanAPI.ts +17 -0
  24. package/Server/Infrastructure/GlobalCache.ts +8 -2
  25. package/Server/Infrastructure/Postgres/SchemaMigrations/1781500000000-AddProxmoxAndCephClusterTables.ts +163 -0
  26. package/Server/Infrastructure/Postgres/SchemaMigrations/1781600000000-AddProxmoxCephV2Columns.ts +211 -0
  27. package/Server/Infrastructure/Postgres/SchemaMigrations/1781600000001-AddProxmoxCephActivityAndRules.ts +590 -0
  28. package/Server/Infrastructure/Postgres/SchemaMigrations/1781700000000-AddProxmoxCephV3Columns.ts +64 -0
  29. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
  30. package/Server/Infrastructure/Redis.ts +40 -12
  31. package/Server/Services/AnalyticsDatabaseService.ts +1 -1
  32. package/Server/Services/BillingService.ts +109 -21
  33. package/Server/Services/CephClusterLabelRuleEngineService.ts +200 -0
  34. package/Server/Services/CephClusterLabelRuleService.ts +14 -0
  35. package/Server/Services/CephClusterOwnerRuleEngineService.ts +218 -0
  36. package/Server/Services/CephClusterOwnerRuleService.ts +14 -0
  37. package/Server/Services/CephClusterOwnerTeamService.ts +10 -0
  38. package/Server/Services/CephClusterOwnerUserService.ts +10 -0
  39. package/Server/Services/CephClusterService.ts +401 -0
  40. package/Server/Services/CephResourceService.ts +383 -0
  41. package/Server/Services/CloudResourceService.ts +11 -3
  42. package/Server/Services/DockerHostService.ts +11 -3
  43. package/Server/Services/ExceptionAggregationService.ts +2 -0
  44. package/Server/Services/HostService.ts +11 -3
  45. package/Server/Services/Index.ts +24 -0
  46. package/Server/Services/KubernetesClusterService.ts +11 -3
  47. package/Server/Services/LogAggregationService.ts +2 -0
  48. package/Server/Services/MetricAggregationService.ts +2 -0
  49. package/Server/Services/OpenTelemetryIngestService.ts +36 -0
  50. package/Server/Services/ProxmoxClusterLabelRuleEngineService.ts +204 -0
  51. package/Server/Services/ProxmoxClusterLabelRuleService.ts +14 -0
  52. package/Server/Services/ProxmoxClusterOwnerRuleEngineService.ts +222 -0
  53. package/Server/Services/ProxmoxClusterOwnerRuleService.ts +14 -0
  54. package/Server/Services/ProxmoxClusterOwnerTeamService.ts +10 -0
  55. package/Server/Services/ProxmoxClusterOwnerUserService.ts +10 -0
  56. package/Server/Services/ProxmoxClusterService.ts +382 -0
  57. package/Server/Services/ProxmoxResourceService.ts +404 -0
  58. package/Server/Services/RumApplicationService.ts +11 -3
  59. package/Server/Services/ServerlessFunctionService.ts +11 -3
  60. package/Server/Services/TelemetryUsageBillingService.ts +41 -3
  61. package/Server/Services/TraceAggregationService.ts +2 -0
  62. package/Server/Types/AnalyticsDatabase/AggregateBy.ts +8 -23
  63. package/Server/Utils/Monitor/MonitorAlert.ts +45 -0
  64. package/Server/Utils/Monitor/MonitorClusterContext.ts +129 -0
  65. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +344 -4
  66. package/Server/Utils/Monitor/MonitorIncident.ts +130 -7
  67. package/Server/Utils/Monitor/MonitorMaintenanceSuppression.ts +39 -6
  68. package/Server/Utils/Monitor/MonitorTemplateUtil.ts +3 -1
  69. package/Server/Utils/Monitor/SeriesResourceLabels.ts +33 -0
  70. package/Server/Utils/Profiling.ts +37 -2
  71. package/Server/Utils/Telemetry/EntityRegistry.ts +4 -0
  72. package/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.ts +1096 -0
  73. package/Server/Utils/Telemetry/TelemetryEntity.ts +85 -0
  74. package/Server/Utils/Telemetry.ts +8 -19
  75. package/Tests/Server/API/BillingInvoiceAPI.test.ts +194 -0
  76. package/Tests/Server/API/ProjectAPI.test.ts +91 -0
  77. package/Tests/Server/API/ResellerPlanAPI.test.ts +207 -0
  78. package/Tests/Server/Infrastructure/GlobalCache.test.ts +100 -0
  79. package/Tests/Server/Services/BillingService.test.ts +323 -0
  80. package/Tests/Server/Services/CephResourceService.test.ts +264 -0
  81. package/Tests/Server/Services/ProxmoxResourceService.test.ts +326 -0
  82. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +322 -0
  83. package/Tests/Server/Utils/Monitor/MonitorMaintenanceSuppression.test.ts +13 -0
  84. package/Tests/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.test.ts +879 -0
  85. package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +196 -0
  86. package/Tests/Types/Monitor/CephAlertTemplates.test.ts +1231 -0
  87. package/Tests/Types/Monitor/ProxmoxAlertTemplates.test.ts +732 -0
  88. package/Tests/Utils/ModelImportExport.test.ts +366 -0
  89. package/Tests/Utils/Telemetry/EntityRelationship.test.ts +49 -0
  90. package/Tests/Utils/Telemetry/HeartbeatAvailability.test.ts +423 -0
  91. package/Types/BaseDatabase/AggregationIntervalUtil.ts +74 -0
  92. package/Types/Dashboard/DashboardComponentType.ts +4 -0
  93. package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +2 -0
  94. package/Types/Dashboard/DashboardComponents/DashboardCephOsdListComponent.ts +15 -0
  95. package/Types/Dashboard/DashboardComponents/DashboardCephPoolListComponent.ts +14 -0
  96. package/Types/Dashboard/DashboardComponents/DashboardProxmoxGuestListComponent.ts +17 -0
  97. package/Types/Dashboard/DashboardComponents/DashboardProxmoxNodeListComponent.ts +16 -0
  98. package/Types/Dashboard/DashboardTemplates.ts +446 -0
  99. package/Types/Icon/IconProp.ts +2 -0
  100. package/Types/Monitor/CephAlertTemplates.ts +1647 -0
  101. package/Types/Monitor/CephMetricCatalog.ts +409 -0
  102. package/Types/Monitor/MetricMonitor/MetricMonitorResponse.ts +44 -0
  103. package/Types/Monitor/MonitorStep.ts +64 -0
  104. package/Types/Monitor/MonitorStepCephMonitor.ts +57 -0
  105. package/Types/Monitor/MonitorStepProxmoxMonitor.ts +81 -0
  106. package/Types/Monitor/MonitorType.ts +29 -1
  107. package/Types/Monitor/ProxmoxAlertTemplates.ts +899 -0
  108. package/Types/Monitor/ProxmoxMetricCatalog.ts +382 -0
  109. package/Types/Permission.ts +464 -0
  110. package/Types/Telemetry/EntityType.ts +11 -0
  111. package/Types/Telemetry/ServiceType.ts +2 -0
  112. package/UI/Components/Icon/Icon.tsx +84 -0
  113. package/UI/Components/ImportExport/ExportModelCard.tsx +90 -0
  114. package/UI/Components/ImportExport/ImportModelsModal.tsx +239 -0
  115. package/UI/Components/ModelTable/ModelTable.tsx +294 -143
  116. package/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.ts +9 -5
  117. package/UI/Utils/ModelImportExport.ts +207 -0
  118. package/UI/Utils/Telemetry/Telemetry.ts +16 -21
  119. package/UI/Utils/TelemetryService.ts +7 -3
  120. package/Utils/Dashboard/Components/DashboardCephOsdListComponent.ts +63 -0
  121. package/Utils/Dashboard/Components/DashboardCephPoolListComponent.ts +32 -0
  122. package/Utils/Dashboard/Components/DashboardCephResourceListShared.ts +61 -0
  123. package/Utils/Dashboard/Components/DashboardProxmoxGuestListComponent.ts +69 -0
  124. package/Utils/Dashboard/Components/DashboardProxmoxNodeListComponent.ts +55 -0
  125. package/Utils/Dashboard/Components/DashboardProxmoxResourceListShared.ts +61 -0
  126. package/Utils/Dashboard/Components/Index.ts +28 -0
  127. package/Utils/ModelImportExport.ts +369 -0
  128. package/Utils/Telemetry/EntityKey.ts +35 -0
  129. package/Utils/Telemetry/EntityRelationship.ts +6 -0
  130. package/Utils/Telemetry/HeartbeatAvailability.ts +262 -0
  131. package/build/dist/Models/DatabaseModels/Alert.js +108 -0
  132. package/build/dist/Models/DatabaseModels/Alert.js.map +1 -1
  133. package/build/dist/Models/DatabaseModels/CephCluster.js +992 -0
  134. package/build/dist/Models/DatabaseModels/CephCluster.js.map +1 -0
  135. package/build/dist/Models/DatabaseModels/CephClusterLabelRule.js +522 -0
  136. package/build/dist/Models/DatabaseModels/CephClusterLabelRule.js.map +1 -0
  137. package/build/dist/Models/DatabaseModels/CephClusterOwnerRule.js +603 -0
  138. package/build/dist/Models/DatabaseModels/CephClusterOwnerRule.js.map +1 -0
  139. package/build/dist/Models/DatabaseModels/CephClusterOwnerTeam.js +503 -0
  140. package/build/dist/Models/DatabaseModels/CephClusterOwnerTeam.js.map +1 -0
  141. package/build/dist/Models/DatabaseModels/CephClusterOwnerUser.js +502 -0
  142. package/build/dist/Models/DatabaseModels/CephClusterOwnerUser.js.map +1 -0
  143. package/build/dist/Models/DatabaseModels/CephResource.js +846 -0
  144. package/build/dist/Models/DatabaseModels/CephResource.js.map +1 -0
  145. package/build/dist/Models/DatabaseModels/Host.js +63 -0
  146. package/build/dist/Models/DatabaseModels/Host.js.map +1 -1
  147. package/build/dist/Models/DatabaseModels/Incident.js +108 -0
  148. package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
  149. package/build/dist/Models/DatabaseModels/Index.js +24 -0
  150. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  151. package/build/dist/Models/DatabaseModels/ProxmoxCluster.js +967 -0
  152. package/build/dist/Models/DatabaseModels/ProxmoxCluster.js.map +1 -0
  153. package/build/dist/Models/DatabaseModels/ProxmoxClusterLabelRule.js +522 -0
  154. package/build/dist/Models/DatabaseModels/ProxmoxClusterLabelRule.js.map +1 -0
  155. package/build/dist/Models/DatabaseModels/ProxmoxClusterOwnerRule.js +603 -0
  156. package/build/dist/Models/DatabaseModels/ProxmoxClusterOwnerRule.js.map +1 -0
  157. package/build/dist/Models/DatabaseModels/ProxmoxClusterOwnerTeam.js +503 -0
  158. package/build/dist/Models/DatabaseModels/ProxmoxClusterOwnerTeam.js.map +1 -0
  159. package/build/dist/Models/DatabaseModels/ProxmoxClusterOwnerUser.js +502 -0
  160. package/build/dist/Models/DatabaseModels/ProxmoxClusterOwnerUser.js.map +1 -0
  161. package/build/dist/Models/DatabaseModels/ProxmoxResource.js +761 -0
  162. package/build/dist/Models/DatabaseModels/ProxmoxResource.js.map +1 -0
  163. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +108 -0
  164. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
  165. package/build/dist/Server/API/BillingInvoiceAPI.js +35 -5
  166. package/build/dist/Server/API/BillingInvoiceAPI.js.map +1 -1
  167. package/build/dist/Server/API/CephResourceAPI.js +98 -0
  168. package/build/dist/Server/API/CephResourceAPI.js.map +1 -0
  169. package/build/dist/Server/API/DashboardAPI.js +46 -0
  170. package/build/dist/Server/API/DashboardAPI.js.map +1 -1
  171. package/build/dist/Server/API/ProjectAPI.js +11 -0
  172. package/build/dist/Server/API/ProjectAPI.js.map +1 -1
  173. package/build/dist/Server/API/ProxmoxResourceAPI.js +95 -0
  174. package/build/dist/Server/API/ProxmoxResourceAPI.js.map +1 -0
  175. package/build/dist/Server/API/ResellerPlanAPI.js +17 -3
  176. package/build/dist/Server/API/ResellerPlanAPI.js.map +1 -1
  177. package/build/dist/Server/Infrastructure/GlobalCache.js +7 -2
  178. package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
  179. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1781500000000-AddProxmoxAndCephClusterTables.js +76 -0
  180. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1781500000000-AddProxmoxAndCephClusterTables.js.map +1 -0
  181. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1781600000000-AddProxmoxCephV2Columns.js +108 -0
  182. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1781600000000-AddProxmoxCephV2Columns.js.map +1 -0
  183. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1781600000001-AddProxmoxCephActivityAndRules.js +253 -0
  184. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1781600000001-AddProxmoxCephActivityAndRules.js.map +1 -0
  185. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1781700000000-AddProxmoxCephV3Columns.js +43 -0
  186. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1781700000000-AddProxmoxCephV3Columns.js.map +1 -0
  187. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
  188. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  189. package/build/dist/Server/Infrastructure/Redis.js +31 -8
  190. package/build/dist/Server/Infrastructure/Redis.js.map +1 -1
  191. package/build/dist/Server/Services/AnalyticsDatabaseService.js +1 -1
  192. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  193. package/build/dist/Server/Services/BillingService.js +85 -23
  194. package/build/dist/Server/Services/BillingService.js.map +1 -1
  195. package/build/dist/Server/Services/CephClusterLabelRuleEngineService.js +166 -0
  196. package/build/dist/Server/Services/CephClusterLabelRuleEngineService.js.map +1 -0
  197. package/build/dist/Server/Services/CephClusterLabelRuleService.js +13 -0
  198. package/build/dist/Server/Services/CephClusterLabelRuleService.js.map +1 -0
  199. package/build/dist/Server/Services/CephClusterOwnerRuleEngineService.js +186 -0
  200. package/build/dist/Server/Services/CephClusterOwnerRuleEngineService.js.map +1 -0
  201. package/build/dist/Server/Services/CephClusterOwnerRuleService.js +13 -0
  202. package/build/dist/Server/Services/CephClusterOwnerRuleService.js.map +1 -0
  203. package/build/dist/Server/Services/CephClusterOwnerTeamService.js +9 -0
  204. package/build/dist/Server/Services/CephClusterOwnerTeamService.js.map +1 -0
  205. package/build/dist/Server/Services/CephClusterOwnerUserService.js +9 -0
  206. package/build/dist/Server/Services/CephClusterOwnerUserService.js.map +1 -0
  207. package/build/dist/Server/Services/CephClusterService.js +353 -0
  208. package/build/dist/Server/Services/CephClusterService.js.map +1 -0
  209. package/build/dist/Server/Services/CephResourceService.js +257 -0
  210. package/build/dist/Server/Services/CephResourceService.js.map +1 -0
  211. package/build/dist/Server/Services/CloudResourceService.js +10 -2
  212. package/build/dist/Server/Services/CloudResourceService.js.map +1 -1
  213. package/build/dist/Server/Services/DockerHostService.js +10 -2
  214. package/build/dist/Server/Services/DockerHostService.js.map +1 -1
  215. package/build/dist/Server/Services/ExceptionAggregationService.js +2 -0
  216. package/build/dist/Server/Services/ExceptionAggregationService.js.map +1 -1
  217. package/build/dist/Server/Services/HostService.js +10 -2
  218. package/build/dist/Server/Services/HostService.js.map +1 -1
  219. package/build/dist/Server/Services/Index.js +24 -0
  220. package/build/dist/Server/Services/Index.js.map +1 -1
  221. package/build/dist/Server/Services/KubernetesClusterService.js +10 -2
  222. package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
  223. package/build/dist/Server/Services/LogAggregationService.js +2 -0
  224. package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
  225. package/build/dist/Server/Services/MetricAggregationService.js +2 -0
  226. package/build/dist/Server/Services/MetricAggregationService.js.map +1 -1
  227. package/build/dist/Server/Services/OpenTelemetryIngestService.js +37 -7
  228. package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
  229. package/build/dist/Server/Services/ProxmoxClusterLabelRuleEngineService.js +166 -0
  230. package/build/dist/Server/Services/ProxmoxClusterLabelRuleEngineService.js.map +1 -0
  231. package/build/dist/Server/Services/ProxmoxClusterLabelRuleService.js +13 -0
  232. package/build/dist/Server/Services/ProxmoxClusterLabelRuleService.js.map +1 -0
  233. package/build/dist/Server/Services/ProxmoxClusterOwnerRuleEngineService.js +186 -0
  234. package/build/dist/Server/Services/ProxmoxClusterOwnerRuleEngineService.js.map +1 -0
  235. package/build/dist/Server/Services/ProxmoxClusterOwnerRuleService.js +13 -0
  236. package/build/dist/Server/Services/ProxmoxClusterOwnerRuleService.js.map +1 -0
  237. package/build/dist/Server/Services/ProxmoxClusterOwnerTeamService.js +9 -0
  238. package/build/dist/Server/Services/ProxmoxClusterOwnerTeamService.js.map +1 -0
  239. package/build/dist/Server/Services/ProxmoxClusterOwnerUserService.js +9 -0
  240. package/build/dist/Server/Services/ProxmoxClusterOwnerUserService.js.map +1 -0
  241. package/build/dist/Server/Services/ProxmoxClusterService.js +337 -0
  242. package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -0
  243. package/build/dist/Server/Services/ProxmoxResourceService.js +285 -0
  244. package/build/dist/Server/Services/ProxmoxResourceService.js.map +1 -0
  245. package/build/dist/Server/Services/RumApplicationService.js +10 -2
  246. package/build/dist/Server/Services/RumApplicationService.js.map +1 -1
  247. package/build/dist/Server/Services/ServerlessFunctionService.js +10 -2
  248. package/build/dist/Server/Services/ServerlessFunctionService.js.map +1 -1
  249. package/build/dist/Server/Services/TelemetryUsageBillingService.js +30 -3
  250. package/build/dist/Server/Services/TelemetryUsageBillingService.js.map +1 -1
  251. package/build/dist/Server/Services/TraceAggregationService.js +2 -0
  252. package/build/dist/Server/Services/TraceAggregationService.js.map +1 -1
  253. package/build/dist/Server/Types/AnalyticsDatabase/AggregateBy.js +8 -25
  254. package/build/dist/Server/Types/AnalyticsDatabase/AggregateBy.js.map +1 -1
  255. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +36 -0
  256. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  257. package/build/dist/Server/Utils/Monitor/MonitorClusterContext.js +90 -0
  258. package/build/dist/Server/Utils/Monitor/MonitorClusterContext.js.map +1 -0
  259. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +228 -4
  260. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  261. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +103 -8
  262. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  263. package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js +23 -6
  264. package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js.map +1 -1
  265. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +3 -1
  266. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
  267. package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js +23 -0
  268. package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js.map +1 -1
  269. package/build/dist/Server/Utils/Profiling.js +24 -3
  270. package/build/dist/Server/Utils/Profiling.js.map +1 -1
  271. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +4 -0
  272. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
  273. package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js +854 -0
  274. package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js.map +1 -0
  275. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +62 -0
  276. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
  277. package/build/dist/Server/Utils/Telemetry.js +8 -10
  278. package/build/dist/Server/Utils/Telemetry.js.map +1 -1
  279. package/build/dist/Types/BaseDatabase/AggregationIntervalUtil.js +69 -0
  280. package/build/dist/Types/BaseDatabase/AggregationIntervalUtil.js.map +1 -0
  281. package/build/dist/Types/Dashboard/DashboardComponentType.js +4 -0
  282. package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
  283. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +2 -0
  284. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
  285. package/build/dist/Types/Dashboard/DashboardComponents/DashboardCephOsdListComponent.js +2 -0
  286. package/build/dist/Types/Dashboard/DashboardComponents/DashboardCephOsdListComponent.js.map +1 -0
  287. package/build/dist/Types/Dashboard/DashboardComponents/DashboardCephPoolListComponent.js +2 -0
  288. package/build/dist/Types/Dashboard/DashboardComponents/DashboardCephPoolListComponent.js.map +1 -0
  289. package/build/dist/Types/Dashboard/DashboardComponents/DashboardProxmoxGuestListComponent.js +2 -0
  290. package/build/dist/Types/Dashboard/DashboardComponents/DashboardProxmoxGuestListComponent.js.map +1 -0
  291. package/build/dist/Types/Dashboard/DashboardComponents/DashboardProxmoxNodeListComponent.js +2 -0
  292. package/build/dist/Types/Dashboard/DashboardComponents/DashboardProxmoxNodeListComponent.js.map +1 -0
  293. package/build/dist/Types/Dashboard/DashboardTemplates.js +394 -0
  294. package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
  295. package/build/dist/Types/Icon/IconProp.js +2 -0
  296. package/build/dist/Types/Icon/IconProp.js.map +1 -1
  297. package/build/dist/Types/Monitor/CephAlertTemplates.js +1379 -0
  298. package/build/dist/Types/Monitor/CephAlertTemplates.js.map +1 -0
  299. package/build/dist/Types/Monitor/CephMetricCatalog.js +353 -0
  300. package/build/dist/Types/Monitor/CephMetricCatalog.js.map +1 -0
  301. package/build/dist/Types/Monitor/MonitorStep.js +46 -0
  302. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  303. package/build/dist/Types/Monitor/MonitorStepCephMonitor.js +34 -0
  304. package/build/dist/Types/Monitor/MonitorStepCephMonitor.js.map +1 -0
  305. package/build/dist/Types/Monitor/MonitorStepProxmoxMonitor.js +36 -0
  306. package/build/dist/Types/Monitor/MonitorStepProxmoxMonitor.js.map +1 -0
  307. package/build/dist/Types/Monitor/MonitorType.js +27 -1
  308. package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
  309. package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js +743 -0
  310. package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js.map +1 -0
  311. package/build/dist/Types/Monitor/ProxmoxMetricCatalog.js +320 -0
  312. package/build/dist/Types/Monitor/ProxmoxMetricCatalog.js.map +1 -0
  313. package/build/dist/Types/Permission.js +408 -0
  314. package/build/dist/Types/Permission.js.map +1 -1
  315. package/build/dist/Types/Telemetry/EntityType.js +11 -0
  316. package/build/dist/Types/Telemetry/EntityType.js.map +1 -1
  317. package/build/dist/Types/Telemetry/ServiceType.js +2 -0
  318. package/build/dist/Types/Telemetry/ServiceType.js.map +1 -1
  319. package/build/dist/UI/Components/Icon/Icon.js +33 -0
  320. package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
  321. package/build/dist/UI/Components/ImportExport/ExportModelCard.js +50 -0
  322. package/build/dist/UI/Components/ImportExport/ExportModelCard.js.map +1 -0
  323. package/build/dist/UI/Components/ImportExport/ImportModelsModal.js +115 -0
  324. package/build/dist/UI/Components/ImportExport/ImportModelsModal.js.map +1 -0
  325. package/build/dist/UI/Components/ModelTable/ModelTable.js +166 -74
  326. package/build/dist/UI/Components/ModelTable/ModelTable.js.map +1 -1
  327. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js +5 -1
  328. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js.map +1 -1
  329. package/build/dist/UI/Utils/ModelImportExport.js +142 -0
  330. package/build/dist/UI/Utils/ModelImportExport.js.map +1 -0
  331. package/build/dist/UI/Utils/Telemetry/Telemetry.js +11 -10
  332. package/build/dist/UI/Utils/Telemetry/Telemetry.js.map +1 -1
  333. package/build/dist/UI/Utils/TelemetryService.js +5 -2
  334. package/build/dist/UI/Utils/TelemetryService.js.map +1 -1
  335. package/build/dist/Utils/Dashboard/Components/DashboardCephOsdListComponent.js +50 -0
  336. package/build/dist/Utils/Dashboard/Components/DashboardCephOsdListComponent.js.map +1 -0
  337. package/build/dist/Utils/Dashboard/Components/DashboardCephPoolListComponent.js +27 -0
  338. package/build/dist/Utils/Dashboard/Components/DashboardCephPoolListComponent.js.map +1 -0
  339. package/build/dist/Utils/Dashboard/Components/DashboardCephResourceListShared.js +46 -0
  340. package/build/dist/Utils/Dashboard/Components/DashboardCephResourceListShared.js.map +1 -0
  341. package/build/dist/Utils/Dashboard/Components/DashboardProxmoxGuestListComponent.js +55 -0
  342. package/build/dist/Utils/Dashboard/Components/DashboardProxmoxGuestListComponent.js.map +1 -0
  343. package/build/dist/Utils/Dashboard/Components/DashboardProxmoxNodeListComponent.js +42 -0
  344. package/build/dist/Utils/Dashboard/Components/DashboardProxmoxNodeListComponent.js.map +1 -0
  345. package/build/dist/Utils/Dashboard/Components/DashboardProxmoxResourceListShared.js +46 -0
  346. package/build/dist/Utils/Dashboard/Components/DashboardProxmoxResourceListShared.js.map +1 -0
  347. package/build/dist/Utils/Dashboard/Components/Index.js +16 -0
  348. package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
  349. package/build/dist/Utils/ModelImportExport.js +257 -0
  350. package/build/dist/Utils/ModelImportExport.js.map +1 -0
  351. package/build/dist/Utils/Telemetry/EntityKey.js +27 -0
  352. package/build/dist/Utils/Telemetry/EntityKey.js.map +1 -1
  353. package/build/dist/Utils/Telemetry/EntityRelationship.js +3 -0
  354. package/build/dist/Utils/Telemetry/EntityRelationship.js.map +1 -1
  355. package/build/dist/Utils/Telemetry/HeartbeatAvailability.js +174 -0
  356. package/build/dist/Utils/Telemetry/HeartbeatAvailability.js.map +1 -0
  357. package/package.json +29 -21
@@ -0,0 +1,326 @@
1
+ import ProxmoxResourceService, {
2
+ ParsedProxmoxResource,
3
+ ProxmoxResourceLatestMetric,
4
+ } from "../../../Server/Services/ProxmoxResourceService";
5
+ import ObjectID from "../../../Types/ObjectID";
6
+
7
+ /*
8
+ * WI-21: the ProxmoxResource inventory write path. The service builds
9
+ * raw parameterized SQL against the TypeORM manager — these tests mock
10
+ * the query runner (no Postgres) and lock in the statement shape:
11
+ *
12
+ * - COALESCE per identity/status column, so a batch that lacks an
13
+ * info series (or the WI-24 backup-info collector) keeps the
14
+ * last-known value instead of blanking it,
15
+ * - the lastSeenAt dominance guard (out-of-order ingest never
16
+ * regresses a newer snapshot),
17
+ * - parameter tuples in exact column order — the INSERT column list
18
+ * and the params must never drift,
19
+ * - 500-row chunking,
20
+ * - NULL metric values stay NULL (a qemu guest without the guest
21
+ * agent must never read 0 disk).
22
+ */
23
+
24
+ const PROJECT_ID: ObjectID = ObjectID.generate();
25
+ const CLUSTER_ID: ObjectID = ObjectID.generate();
26
+
27
+ type QueryCall = [string, Array<unknown>];
28
+
29
+ function mockQueryRunner(result: unknown = []): jest.Mock {
30
+ const query: jest.Mock = jest.fn().mockResolvedValue(result);
31
+ jest
32
+ .spyOn(ProxmoxResourceService, "getRepository")
33
+ .mockReturnValue({ manager: { query } } as any);
34
+ return query;
35
+ }
36
+
37
+ function parsedResource(
38
+ overrides: Partial<ParsedProxmoxResource> = {},
39
+ ): ParsedProxmoxResource {
40
+ return {
41
+ kind: "Guest",
42
+ externalId: "qemu/100",
43
+ name: "web-vm",
44
+ vmid: 100,
45
+ guestType: "qemu",
46
+ parentNodeName: "pve1",
47
+ isUp: true,
48
+ haState: null,
49
+ onboot: true,
50
+ isBackedUp: null,
51
+ uptimeSeconds: 3600,
52
+ lastSeenAt: new Date("2026-06-13T00:00:00.000Z"),
53
+ ...overrides,
54
+ };
55
+ }
56
+
57
+ afterEach(() => {
58
+ jest.restoreAllMocks();
59
+ });
60
+
61
+ describe("ProxmoxResourceService.bulkUpsert", () => {
62
+ test("does nothing for an empty batch", async () => {
63
+ const query: jest.Mock = mockQueryRunner();
64
+ await ProxmoxResourceService.bulkUpsert({
65
+ projectId: PROJECT_ID,
66
+ proxmoxClusterId: CLUSTER_ID,
67
+ resources: [],
68
+ });
69
+ expect(query).not.toHaveBeenCalled();
70
+ });
71
+
72
+ test("emits an ON CONFLICT upsert with COALESCE per identity/status column", async () => {
73
+ const query: jest.Mock = mockQueryRunner();
74
+ await ProxmoxResourceService.bulkUpsert({
75
+ projectId: PROJECT_ID,
76
+ proxmoxClusterId: CLUSTER_ID,
77
+ resources: [parsedResource()],
78
+ });
79
+
80
+ expect(query).toHaveBeenCalledTimes(1);
81
+ const [sql] = query.mock.calls[0] as QueryCall;
82
+
83
+ expect(sql).toContain('INSERT INTO "ProxmoxResource"');
84
+ expect(sql).toContain(
85
+ 'ON CONFLICT ("projectId", "proxmoxClusterId", "kind", "externalId")',
86
+ );
87
+
88
+ /*
89
+ * Every identity/status column COALESCEs against the existing row —
90
+ * a partial batch must never blank a previously learned value.
91
+ */
92
+ for (const column of [
93
+ "name",
94
+ "vmid",
95
+ "guestType",
96
+ "parentNodeName",
97
+ "isUp",
98
+ "haState",
99
+ "onboot",
100
+ "isBackedUp",
101
+ "uptimeSeconds",
102
+ ]) {
103
+ expect(sql).toContain(
104
+ `"${column}" = COALESCE(EXCLUDED."${column}", "ProxmoxResource"."${column}")`,
105
+ );
106
+ }
107
+
108
+ // lastSeenAt is overwritten (not COALESCEd) under the dominance guard.
109
+ expect(sql).toContain('"lastSeenAt" = EXCLUDED."lastSeenAt"');
110
+ expect(sql).toContain(
111
+ 'WHERE EXCLUDED."lastSeenAt" >= "ProxmoxResource"."lastSeenAt"',
112
+ );
113
+ });
114
+
115
+ test("parameter tuple matches the INSERT column order exactly", async () => {
116
+ const query: jest.Mock = mockQueryRunner();
117
+ const lastSeenAt: Date = new Date("2026-06-13T00:00:00.000Z");
118
+ await ProxmoxResourceService.bulkUpsert({
119
+ projectId: PROJECT_ID,
120
+ proxmoxClusterId: CLUSTER_ID,
121
+ resources: [
122
+ parsedResource({
123
+ isBackedUp: false,
124
+ uptimeSeconds: 3600.9,
125
+ lastSeenAt,
126
+ }),
127
+ ],
128
+ });
129
+
130
+ const [sql, params] = query.mock.calls[0] as QueryCall;
131
+
132
+ // 15 columns per row: a 1-row batch carries exactly $1..$15.
133
+ expect(params).toHaveLength(15);
134
+ expect(sql).toContain(
135
+ "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)",
136
+ );
137
+ expect(params).toEqual([
138
+ PROJECT_ID.toString(),
139
+ CLUSTER_ID.toString(),
140
+ "Guest",
141
+ "qemu/100",
142
+ "web-vm",
143
+ 100,
144
+ "qemu",
145
+ "pve1",
146
+ true,
147
+ null, // haState
148
+ true, // onboot
149
+ false, // isBackedUp (WI-24) — false is a value, distinct from null
150
+ 3600, // uptimeSeconds truncated
151
+ lastSeenAt,
152
+ 0, // version
153
+ ]);
154
+ });
155
+
156
+ test("tri-state isBackedUp: null rides through so COALESCE keeps the last-known flag", async () => {
157
+ const query: jest.Mock = mockQueryRunner();
158
+ await ProxmoxResourceService.bulkUpsert({
159
+ projectId: PROJECT_ID,
160
+ proxmoxClusterId: CLUSTER_ID,
161
+ resources: [parsedResource({ isBackedUp: null })],
162
+ });
163
+
164
+ const [, params] = query.mock.calls[0] as QueryCall;
165
+ // Column 12 (0-indexed 11) is isBackedUp.
166
+ expect(params[11]).toBeNull();
167
+ });
168
+
169
+ test("chunks batches of more than 500 rows into multiple statements", async () => {
170
+ const query: jest.Mock = mockQueryRunner();
171
+ const resources: Array<ParsedProxmoxResource> = [];
172
+ for (let i: number = 0; i < 501; i++) {
173
+ resources.push(parsedResource({ externalId: `qemu/${i}`, vmid: i }));
174
+ }
175
+
176
+ await ProxmoxResourceService.bulkUpsert({
177
+ projectId: PROJECT_ID,
178
+ proxmoxClusterId: CLUSTER_ID,
179
+ resources,
180
+ });
181
+
182
+ expect(query).toHaveBeenCalledTimes(2);
183
+ const [, firstParams] = query.mock.calls[0] as QueryCall;
184
+ const [, secondParams] = query.mock.calls[1] as QueryCall;
185
+ expect(firstParams).toHaveLength(500 * 15);
186
+ expect(secondParams).toHaveLength(15);
187
+ });
188
+ });
189
+
190
+ describe("ProxmoxResourceService.bulkUpdateLatestMetrics", () => {
191
+ function latestMetric(
192
+ overrides: Partial<ProxmoxResourceLatestMetric> = {},
193
+ ): ProxmoxResourceLatestMetric {
194
+ return {
195
+ kind: "Guest",
196
+ externalId: "qemu/100",
197
+ cpuPercent: 12.5,
198
+ memoryBytes: 1024,
199
+ maxMemoryBytes: 2048,
200
+ memoryPercent: 50,
201
+ diskBytes: null,
202
+ maxDiskBytes: null,
203
+ observedAt: new Date("2026-06-13T00:00:00.000Z"),
204
+ ...overrides,
205
+ };
206
+ }
207
+
208
+ test("does nothing for an empty batch", async () => {
209
+ const query: jest.Mock = mockQueryRunner();
210
+ await ProxmoxResourceService.bulkUpdateLatestMetrics({
211
+ projectId: PROJECT_ID,
212
+ proxmoxClusterId: CLUSTER_ID,
213
+ metrics: [],
214
+ });
215
+ expect(query).not.toHaveBeenCalled();
216
+ });
217
+
218
+ test("COALESCEs every mirror column and guards on metricsUpdatedAt", async () => {
219
+ const query: jest.Mock = mockQueryRunner();
220
+ await ProxmoxResourceService.bulkUpdateLatestMetrics({
221
+ projectId: PROJECT_ID,
222
+ proxmoxClusterId: CLUSTER_ID,
223
+ metrics: [latestMetric()],
224
+ });
225
+
226
+ const [sql] = query.mock.calls[0] as QueryCall;
227
+ expect(sql).toContain('UPDATE "ProxmoxResource"');
228
+ for (const [column, alias] of [
229
+ ["latestCpuPercent", "cpu"],
230
+ ["latestMemoryBytes", "mem"],
231
+ ["maxMemoryBytes", "maxMem"],
232
+ ["latestMemoryPercent", "memPct"],
233
+ ["latestDiskBytes", "disk"],
234
+ ["maxDiskBytes", "maxDisk"],
235
+ ]) {
236
+ expect(sql).toContain(
237
+ `"${column}" = COALESCE(v."${alias}", p."${column}")`,
238
+ );
239
+ }
240
+ // Out-of-order points never regress a newer observation.
241
+ expect(sql).toContain(
242
+ '(p."metricsUpdatedAt" IS NULL OR v."observedAt" >= p."metricsUpdatedAt")',
243
+ );
244
+ });
245
+
246
+ test("missing disk series stays NULL — never coerced to 0 (qemu without guest agent)", async () => {
247
+ const query: jest.Mock = mockQueryRunner();
248
+ await ProxmoxResourceService.bulkUpdateLatestMetrics({
249
+ projectId: PROJECT_ID,
250
+ proxmoxClusterId: CLUSTER_ID,
251
+ metrics: [latestMetric({ diskBytes: null, maxDiskBytes: null })],
252
+ });
253
+
254
+ const [, params] = query.mock.calls[0] as QueryCall;
255
+ /*
256
+ * Params: projectId, clusterId, then per row
257
+ * (kind, externalId, cpu, mem, maxMem, memPct, disk, maxDisk, observedAt).
258
+ */
259
+ expect(params).toHaveLength(2 + 9);
260
+ expect(params[2]).toBe("Guest");
261
+ expect(params[3]).toBe("qemu/100");
262
+ expect(params[8]).toBeNull(); // disk
263
+ expect(params[9]).toBeNull(); // maxDisk
264
+ // Bigint columns are sent as strings to avoid JS precision loss.
265
+ expect(params[5]).toBe("1024");
266
+ expect(params[6]).toBe("2048");
267
+ });
268
+ });
269
+
270
+ describe("ProxmoxResourceService.deleteStaleForCluster", () => {
271
+ test("normalizes the postgres [rows, affected] DELETE result", async () => {
272
+ mockQueryRunner([[], 7]);
273
+ const affected: number = await ProxmoxResourceService.deleteStaleForCluster(
274
+ {
275
+ proxmoxClusterId: CLUSTER_ID,
276
+ olderThan: new Date("2026-06-13T00:00:00.000Z"),
277
+ },
278
+ );
279
+ expect(affected).toBe(7);
280
+ });
281
+
282
+ test("returns 0 when the driver result carries no affected count", async () => {
283
+ mockQueryRunner({});
284
+ const affected: number = await ProxmoxResourceService.deleteStaleForCluster(
285
+ {
286
+ proxmoxClusterId: CLUSTER_ID,
287
+ olderThan: new Date("2026-06-13T00:00:00.000Z"),
288
+ },
289
+ );
290
+ expect(affected).toBe(0);
291
+ });
292
+ });
293
+
294
+ describe("ProxmoxResourceService.getStaleThresholdMinutes", () => {
295
+ const ENV_KEY: string = "PVE_INVENTORY_STALE_MINUTES";
296
+ let savedValue: string | undefined;
297
+
298
+ beforeEach(() => {
299
+ savedValue = process.env[ENV_KEY];
300
+ });
301
+
302
+ afterEach(() => {
303
+ if (savedValue === undefined) {
304
+ delete process.env[ENV_KEY];
305
+ } else {
306
+ process.env[ENV_KEY] = savedValue;
307
+ }
308
+ });
309
+
310
+ test("defaults to 15 minutes (3x the 5-minute scrape interval)", () => {
311
+ delete process.env[ENV_KEY];
312
+ expect(ProxmoxResourceService.getStaleThresholdMinutes()).toBe(15);
313
+ });
314
+
315
+ test("honors the env override", () => {
316
+ process.env[ENV_KEY] = "30";
317
+ expect(ProxmoxResourceService.getStaleThresholdMinutes()).toBe(30);
318
+ });
319
+
320
+ test("rejects overrides below the 5-minute floor and non-numbers", () => {
321
+ process.env[ENV_KEY] = "2";
322
+ expect(ProxmoxResourceService.getStaleThresholdMinutes()).toBe(15);
323
+ process.env[ENV_KEY] = "not-a-number";
324
+ expect(ProxmoxResourceService.getStaleThresholdMinutes()).toBe(15);
325
+ });
326
+ });
@@ -0,0 +1,322 @@
1
+ import MonitorCriteriaEvaluator from "../../../../Server/Utils/Monitor/MonitorCriteriaEvaluator";
2
+ import Monitor from "../../../../Models/DatabaseModels/Monitor";
3
+ import MonitorStep from "../../../../Types/Monitor/MonitorStep";
4
+ import MetricMonitorResponse, {
5
+ ProxmoxAffectedResource,
6
+ CephAffectedResource,
7
+ } from "../../../../Types/Monitor/MetricMonitor/MetricMonitorResponse";
8
+ import {
9
+ getProxmoxAlertTemplateById,
10
+ ProxmoxAlertTemplate,
11
+ } from "../../../../Types/Monitor/ProxmoxAlertTemplates";
12
+ import {
13
+ getCephAlertTemplateById,
14
+ CephAlertTemplate,
15
+ } from "../../../../Types/Monitor/CephAlertTemplates";
16
+ import ObjectID from "../../../../Types/ObjectID";
17
+
18
+ /*
19
+ * WI-21 monitor-routing seam: the worker-side monitorProxmox /
20
+ * monitorCeph functions (App/FeatureSet/Workers) attach a
21
+ * proxmoxResourceBreakdown / cephResourceBreakdown to the
22
+ * MetricMonitorResponse; MonitorCriteriaEvaluator renders that into
23
+ * the incident root-cause context. These tests drive the evaluator's
24
+ * Proxmox/Ceph branches directly and lock in the render contract:
25
+ *
26
+ * - Proxmox table is Resource / Type / Node / Value; Ceph table is
27
+ * Daemon / Pool / Host / Value,
28
+ * - zero-value rows are dropped (supplementary context — the
29
+ * per-series criteria still alert on them), worst value first,
30
+ * top 10 with an "... and N more" suffix,
31
+ * - identity-less (cluster-wide) breakdowns render NO table and fall
32
+ * back to the metric summary,
33
+ * - the cluster context lines surface the monitor step's
34
+ * clusterIdentifier and resource filters (the pve.scope / pve.id /
35
+ * ceph_daemon / pool_id equality filters the worker maps to).
36
+ *
37
+ * The monitor steps come from the REAL alert templates so this also
38
+ * covers the template → evaluator hand-off shape end to end.
39
+ */
40
+
41
+ type EvaluatorPrivate = {
42
+ buildProxmoxRootCauseContext: (input: {
43
+ dataToProcess: unknown;
44
+ monitorStep: MonitorStep;
45
+ monitor: Monitor;
46
+ }) => string | null;
47
+ buildCephRootCauseContext: (input: {
48
+ dataToProcess: unknown;
49
+ monitorStep: MonitorStep;
50
+ monitor: Monitor;
51
+ }) => string | null;
52
+ };
53
+
54
+ const Evaluator: EvaluatorPrivate =
55
+ MonitorCriteriaEvaluator as unknown as EvaluatorPrivate;
56
+
57
+ function templateArgs(): any {
58
+ return {
59
+ clusterIdentifier: "prod-cluster",
60
+ onlineMonitorStatusId: ObjectID.generate(),
61
+ offlineMonitorStatusId: ObjectID.generate(),
62
+ defaultIncidentSeverityId: ObjectID.generate(),
63
+ defaultAlertSeverityId: ObjectID.generate(),
64
+ monitorName: "Test Monitor",
65
+ };
66
+ }
67
+
68
+ function proxmoxStep(): MonitorStep {
69
+ const template: ProxmoxAlertTemplate | undefined =
70
+ getProxmoxAlertTemplateById("pve-node-offline");
71
+ if (!template) {
72
+ throw new Error("pve-node-offline template missing");
73
+ }
74
+ return template.getMonitorStep(templateArgs());
75
+ }
76
+
77
+ function cephStep(): MonitorStep {
78
+ const template: CephAlertTemplate | undefined =
79
+ getCephAlertTemplateById("ceph-osd-down");
80
+ if (!template) {
81
+ throw new Error("ceph-osd-down template missing");
82
+ }
83
+ return template.getMonitorStep(templateArgs());
84
+ }
85
+
86
+ function metricResponse(
87
+ overrides: Partial<MetricMonitorResponse> = {},
88
+ ): MetricMonitorResponse {
89
+ return {
90
+ projectId: ObjectID.generate(),
91
+ monitorId: ObjectID.generate(),
92
+ metricResult: [],
93
+ metricViewConfig: { queryConfigs: [], formulaConfigs: [] },
94
+ ...overrides,
95
+ };
96
+ }
97
+
98
+ describe("MonitorCriteriaEvaluator - Proxmox root cause breakdown", () => {
99
+ test("renders the Resource/Type/Node/Value table: zero rows dropped, worst first", () => {
100
+ const affectedResources: Array<ProxmoxAffectedResource> = [
101
+ {
102
+ resourceId: "qemu/100",
103
+ resourceName: "web-vm",
104
+ resourceType: "qemu",
105
+ scope: "guest",
106
+ nodeName: "pve1",
107
+ metricValue: 42,
108
+ },
109
+ {
110
+ resourceId: "qemu/101",
111
+ resourceName: "db-vm",
112
+ resourceType: "qemu",
113
+ scope: "guest",
114
+ nodeName: "pve2",
115
+ metricValue: 97,
116
+ },
117
+ // Zero-value row — must be dropped from the table.
118
+ {
119
+ resourceId: "qemu/102",
120
+ resourceName: "idle-vm",
121
+ resourceType: "qemu",
122
+ scope: "guest",
123
+ nodeName: "pve1",
124
+ metricValue: 0,
125
+ },
126
+ ];
127
+
128
+ const context: string | null = Evaluator.buildProxmoxRootCauseContext({
129
+ dataToProcess: metricResponse({
130
+ proxmoxResourceBreakdown: {
131
+ clusterName: "prod-cluster",
132
+ metricName: "pve_cpu_usage_ratio",
133
+ metricFriendlyName: "CPU Usage",
134
+ affectedResources,
135
+ attributes: {},
136
+ },
137
+ }),
138
+ monitorStep: proxmoxStep(),
139
+ monitor: new Monitor(),
140
+ });
141
+
142
+ expect(context).not.toBeNull();
143
+ expect(context).toContain("**Proxmox Cluster Details**");
144
+ expect(context).toContain("- Cluster: prod-cluster");
145
+ expect(context).toContain("- Metric: CPU Usage (`pve_cpu_usage_ratio`)");
146
+
147
+ expect(context).toContain("| Resource | Type | Node | Value |");
148
+ // Worst (97) sorts above 42; the zero row is gone entirely.
149
+ const dbIndex: number = context!.indexOf("`db-vm` (`qemu/101`)");
150
+ const webIndex: number = context!.indexOf("`web-vm` (`qemu/100`)");
151
+ expect(dbIndex).toBeGreaterThan(-1);
152
+ expect(webIndex).toBeGreaterThan(dbIndex);
153
+ expect(context).not.toContain("idle-vm");
154
+ expect(context).toContain("**Affected Resources** (2 total)");
155
+ expect(context).toContain(
156
+ "| `db-vm` (`qemu/101`) | qemu | `pve2` | **97** |",
157
+ );
158
+ });
159
+
160
+ test("caps the table at 10 rows, worst first, with an overflow suffix", () => {
161
+ const affectedResources: Array<ProxmoxAffectedResource> = [];
162
+ for (let i: number = 1; i <= 12; i++) {
163
+ affectedResources.push({
164
+ resourceId: `qemu/${100 + i}`,
165
+ resourceType: "qemu",
166
+ scope: "guest",
167
+ metricValue: i,
168
+ });
169
+ }
170
+
171
+ const context: string | null = Evaluator.buildProxmoxRootCauseContext({
172
+ dataToProcess: metricResponse({
173
+ proxmoxResourceBreakdown: {
174
+ clusterName: "prod-cluster",
175
+ metricName: "pve_cpu_usage_ratio",
176
+ metricFriendlyName: "CPU Usage",
177
+ affectedResources,
178
+ attributes: {},
179
+ },
180
+ }),
181
+ monitorStep: proxmoxStep(),
182
+ monitor: new Monitor(),
183
+ });
184
+
185
+ expect(context).toContain("**Affected Resources** (12 total)");
186
+ expect(context).toContain("*... and 2 more affected resources*");
187
+ // The worst rows survive the cap; the mildest two are cut.
188
+ expect(context).toContain("**12**");
189
+ expect(context).toContain("**3**");
190
+ expect(context).not.toContain("| `qemu/102` | qemu | - | **2** |");
191
+ expect(context).not.toContain("| `qemu/101` | qemu | - | **1** |");
192
+ });
193
+
194
+ test("identity-less (cluster-wide) breakdowns render no table and fall back to the metric summary", () => {
195
+ const context: string | null = Evaluator.buildProxmoxRootCauseContext({
196
+ dataToProcess: metricResponse({
197
+ proxmoxResourceBreakdown: {
198
+ clusterName: "prod-cluster",
199
+ metricName: "pve_not_backed_up_total",
200
+ metricFriendlyName: "Guests Without Backup",
201
+ affectedResources: [
202
+ // WI-24 cluster gauge: a value but NO identity labels.
203
+ { metricValue: 3 },
204
+ ],
205
+ attributes: {},
206
+ },
207
+ metricResult: [{ data: [{}, {}] } as any],
208
+ }),
209
+ monitorStep: proxmoxStep(),
210
+ monitor: new Monitor(),
211
+ });
212
+
213
+ expect(context).not.toContain("| Resource | Type | Node | Value |");
214
+ expect(context).toContain("**Metric Summary**");
215
+ expect(context).toContain("- 2 metric data point(s) returned");
216
+ });
217
+
218
+ test("surfaces the worker's resource-filter mapping (pve.scope / pve.id) in the cluster context", () => {
219
+ const step: MonitorStep = proxmoxStep();
220
+ /*
221
+ * The worker maps resourceFilters to pve.scope / pve.id equality
222
+ * attributes; the evaluator surfaces the same filters so the
223
+ * incident shows WHAT was scoped.
224
+ */
225
+ step.data!.proxmoxMonitor!.resourceFilters = {
226
+ scope: "guest" as any,
227
+ pveId: "100",
228
+ };
229
+
230
+ const context: string | null = Evaluator.buildProxmoxRootCauseContext({
231
+ dataToProcess: metricResponse(),
232
+ monitorStep: step,
233
+ monitor: new Monitor(),
234
+ });
235
+
236
+ expect(context).toContain("- Cluster: prod-cluster");
237
+ expect(context).toContain("- Scope Filter: guest");
238
+ expect(context).toContain("- Resource ID Filter: 100");
239
+ // No breakdown attached: the metric name comes from the step's query.
240
+ expect(context).toContain("- Metric: `pve_up`");
241
+ });
242
+ });
243
+
244
+ describe("MonitorCriteriaEvaluator - Ceph root cause breakdown", () => {
245
+ test("renders the Daemon/Pool/Host/Value table with pool name+id cells", () => {
246
+ const affectedResources: Array<CephAffectedResource> = [
247
+ { daemon: "osd.3", hostname: "ceph-node-1", metricValue: 250 },
248
+ { poolId: "2", poolName: "rbd", metricValue: 91 },
249
+ // Zero row dropped.
250
+ { daemon: "osd.5", hostname: "ceph-node-2", metricValue: 0 },
251
+ ];
252
+
253
+ const context: string | null = Evaluator.buildCephRootCauseContext({
254
+ dataToProcess: metricResponse({
255
+ cephResourceBreakdown: {
256
+ clusterName: "prod-cluster",
257
+ metricName: "ceph_osd_apply_latency_ms",
258
+ metricFriendlyName: "OSD Apply Latency",
259
+ affectedResources,
260
+ attributes: {},
261
+ },
262
+ }),
263
+ monitorStep: cephStep(),
264
+ monitor: new Monitor(),
265
+ });
266
+
267
+ expect(context).toContain("**Ceph Cluster Details**");
268
+ expect(context).toContain("- Cluster: prod-cluster");
269
+ expect(context).toContain(
270
+ "- Metric: OSD Apply Latency (`ceph_osd_apply_latency_ms`)",
271
+ );
272
+
273
+ expect(context).toContain("| Daemon | Pool | Host | Value |");
274
+ expect(context).toContain("| `osd.3` | - | `ceph-node-1` | **250** |");
275
+ expect(context).toContain("| - | `rbd` (`2`) | - | **91** |");
276
+ expect(context).not.toContain("osd.5");
277
+ expect(context).toContain("**Affected Resources** (2 total)");
278
+
279
+ // Worst-first ordering: the 250 row precedes the 91 row.
280
+ const osdIndex: number = context!.indexOf("`osd.3`");
281
+ const poolIndex: number = context!.indexOf("`rbd` (`2`)");
282
+ expect(osdIndex).toBeGreaterThan(-1);
283
+ expect(poolIndex).toBeGreaterThan(osdIndex);
284
+ });
285
+
286
+ test("cluster-wide series (ceph_health_status) render no table", () => {
287
+ const context: string | null = Evaluator.buildCephRootCauseContext({
288
+ dataToProcess: metricResponse({
289
+ cephResourceBreakdown: {
290
+ clusterName: "prod-cluster",
291
+ metricName: "ceph_health_status",
292
+ metricFriendlyName: "Cluster Health",
293
+ affectedResources: [{ metricValue: 2 }],
294
+ attributes: {},
295
+ },
296
+ }),
297
+ monitorStep: cephStep(),
298
+ monitor: new Monitor(),
299
+ });
300
+
301
+ expect(context).toContain("- Cluster: prod-cluster");
302
+ expect(context).not.toContain("| Daemon | Pool | Host | Value |");
303
+ });
304
+
305
+ test("surfaces the worker's resource-filter mapping (ceph_daemon / pool_id) in the cluster context", () => {
306
+ const step: MonitorStep = cephStep();
307
+ step.data!.cephMonitor!.resourceFilters = {
308
+ osdId: "osd.3",
309
+ poolId: "2",
310
+ };
311
+
312
+ const context: string | null = Evaluator.buildCephRootCauseContext({
313
+ dataToProcess: metricResponse(),
314
+ monitorStep: step,
315
+ monitor: new Monitor(),
316
+ });
317
+
318
+ expect(context).toContain("- OSD Filter: osd.3");
319
+ expect(context).toContain("- Pool ID Filter: 2");
320
+ expect(context).toContain("- Metric: `ceph_osd_up`");
321
+ });
322
+ });
@@ -12,6 +12,8 @@ function emptyMaintained(): MaintainedResourceKeys {
12
12
  hosts: { ids: new Set<string>(), names: new Set<string>() },
13
13
  dockerHosts: { ids: new Set<string>(), names: new Set<string>() },
14
14
  kubernetesClusters: { ids: new Set<string>(), names: new Set<string>() },
15
+ proxmoxClusters: { ids: new Set<string>(), names: new Set<string>() },
16
+ cephClusters: { ids: new Set<string>(), names: new Set<string>() },
15
17
  services: { ids: new Set<string>(), names: new Set<string>() },
16
18
  };
17
19
  }
@@ -91,6 +93,17 @@ describe("SeriesResourceLabels", () => {
91
93
  expect(refs.kubernetesClusterNames).toEqual(["c1"]);
92
94
  expect(refs.serviceNames).toEqual(["s1"]);
93
95
  });
96
+
97
+ it("maps proxmox and ceph cluster name keys (prefixed and unprefixed)", () => {
98
+ const refs: SeriesResourceRefs = SeriesResourceLabels.extractResourceRefs(
99
+ {
100
+ "resource.proxmox.cluster.name": "pve-1",
101
+ "ceph.cluster.name": "ceph-1",
102
+ },
103
+ );
104
+ expect(refs.proxmoxClusterNames).toEqual(["pve-1"]);
105
+ expect(refs.cephClusterNames).toEqual(["ceph-1"]);
106
+ });
94
107
  });
95
108
  });
96
109