Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
78d9130
Add Resource Alerts Plugin with DAO, VO, and API Responses
Damans227 Jun 12, 2026
d3fc774
Implement Resource Alerts Manager and related components
Damans227 Jun 16, 2026
70f2945
Add unit tests for Resource Alerts functionality
Damans227 Jun 16, 2026
8e62cb0
Implement email notification feature in Resource Alert Manager
Damans227 Jun 22, 2026
bba5af0
Refactor ResourceAlertManagerImpl to extract publishAlertEvent method…
Damans227 Jun 22, 2026
e98c873
Add Resource Alerts functionality with UI components and localization
Damans227 Jun 23, 2026
18877fc
Implement resource alert rules limit and enhance evaluation logic for…
Damans227 Jun 23, 2026
1bc4923
Change alert icon from AlertOutlined to BellOutlined in resource aler…
Damans227 Jun 23, 2026
7686cf0
Add resource alert rule grouping functionality in configuration
Damans227 Jun 23, 2026
11f6634
Add '/resourcealertrule' endpoint to path matching in ListView component
Damans227 Jun 23, 2026
d08b893
Add scoped styles for form component in CreateResourceAlertRule
Damans227 Jun 23, 2026
b04abcf
Refactor email configuration handling and improve error logging in Re…
Damans227 Jun 24, 2026
8e30245
Add monitoring section and update resource alert labels in localization
Damans227 Jun 24, 2026
985667d
Update icon for monitoring section to BarChartOutlined
Damans227 Jun 24, 2026
02b22cf
Rename resource alert settings to design names and add default repeat…
Damans227 Sep 23, 2026
beabe57
Use resource UUIDs in resource alert APIs and pass resource type from…
Damans227 Sep 23, 2026
4edf66a
Add access checks to resource alert APIs and open them to domain admi…
Damans227 Sep 23, 2026
e67266a
Allow delivering alert payloads directly to selected webhooks
Damans227 Sep 23, 2026
3c1f2f4
Add table, VO and DAO to map resource alert rules to webhooks
Damans227 Sep 23, 2026
85f8eac
Add webhookids to create and update resource alert rule APIs
Damans227 Sep 23, 2026
1b12a67
Deliver fired resource alerts to the rule's webhooks as JSON
Damans227 Sep 23, 2026
0020917
Log resource alert evaluation failures instead of ignoring them
Damans227 Sep 23, 2026
7b93827
Add webhook selection to resource alert rule forms in UI
Damans227 Sep 23, 2026
531a8dc
Add host load average and host network metrics to resource alerts
Damans227 Sep 23, 2026
f6d78f0
Add volume size metric to resource alerts
Damans227 Sep 23, 2026
03a1584
Add storage pool used IOPS metric to resource alerts
Damans227 Sep 23, 2026
52a36ea
Target resource alerts at 24.0.0 after rebase
Damans227 Sep 23, 2026
ef4cc24
Scope generic resource alert rules by owner role: whole cloud, domain…
Damans227 Sep 23, 2026
be9a2b2
Evaluate resource alert rules on a single management server
Damans227 Sep 23, 2026
81a6a29
Remove old fired resource alerts and page listResourceAlerts
Damans227 Sep 23, 2026
7df9396
Remove resource alert rules whose owner or resource no longer exists
Damans227 Sep 23, 2026
e82da56
Record audit events for resource alert rule create, update and delete
Damans227 Sep 23, 2026
6b66d27
Move resource alert commands to the user package and use ApiConstants…
Damans227 Sep 23, 2026
e590118
Validate resource alert rule thresholds and reset interval
Damans227 Sep 23, 2026
10ce4a9
Apply webhook delivery security settings to alert deliveries
Damans227 Sep 23, 2026
fe7bf56
Log alert webhook deliveries that fail inside the delivery thread
Damans227 Sep 23, 2026
26fa206
Return resource name in resource alert rule and alert responses
Damans227 Sep 24, 2026
73943ab
Add resource and owner pickers to resource alert form and show resour…
Damans227 Sep 24, 2026
37d79ab
Round metric values in resource alert tables
Damans227 Sep 24, 2026
32d37ff
Show resource name and UUID in alert email and event bus messages
Damans227 Sep 28, 2026
7bd9199
Show webhook names on resource alert rules
Damans227 Sep 28, 2026
88e8155
Record webhook deliveries for resource alerts
Damans227 Sep 28, 2026
cf7e7ba
Show All resources for generic rules in the rule list
Damans227 Sep 28, 2026
220f254
Merge remote-tracking branch 'upstream/main' into resource-alerts
Damans227 Sep 28, 2026
a4f96e0
Keep proper API error codes for resource alert rule commands
Sep 29, 2026
ae29007
Fall back to the default when the resource alert evaluation interval …
Sep 29, 2026
a8ef272
Only let a rule override all-resources rules of the same owner
Sep 29, 2026
4574edd
Remove alert history when a resource alert rule is removed
Sep 29, 2026
541fa7f
Add rule owner account and domain to resource alert email and event b…
Sep 29, 2026
bd8f007
Hide empty webhooks on rule details and hint the default cooldown
Sep 29, 2026
d7514b5
Add Marvin smoke tests for resource alerts
Sep 29, 2026
6126c2f
Run resource alerts smoke tests in CI
Sep 29, 2026
010b1a5
Skip resources that are stopped or out of service when checking resou…
Sep 29, 2026
f06272c
Remove resource alert rules whose owner lost access to the resource
Sep 29, 2026
9bdfc75
Support projects for resource alert rules
Sep 29, 2026
d34d9fe
Custom edit form for resource alert rules that keeps current webhooks
Sep 29, 2026
fb5ea37
Skip volume disk metrics when the attached VM is not running
Sep 29, 2026
7a4b7fc
Add indexes for resource alert lookups
Damans227 Sep 30, 2026
f559148
Read only the newest recent VM stats when checking resource alert rules
Damans227 Sep 30, 2026
ab3ab0f
Keep disk IO metrics on Instances only, not Volumes
Damans227 Sep 30, 2026
ea7caf6
Send resource alert emails from one thread with a bounded queue
Damans227 Sep 30, 2026
58b9be3
Drop deleted webhooks from resource alert rules
Damans227 Sep 30, 2026
28a87e1
Page and filter resource alerts on the server in alert tabs
Damans227 Sep 30, 2026
23dfab7
Reject blank and duplicate resource alert rule names
Damans227 Sep 30, 2026
e166aae
Allow resource alert messages up to the stored length of 4096
Damans227 Sep 30, 2026
7af9e47
Pick the resource alert type filter from a list and accept any case i…
Damans227 Sep 30, 2026
a510256
Filter fired resource alerts by rule name keyword and reject unknown …
Damans227 Sep 30, 2026
4695cd5
Check the resource alert rule limit and name under a per-account lock
Damans227 Sep 30, 2026
6c82a67
Log fired resource alerts at info level
Damans227 Sep 30, 2026
33b195b
Keep checking other resource alert rules when one rule fails
Damans227 Sep 30, 2026
52d87fa
Allow a few seconds of slack when checking the resource alert cooldown
Damans227 Sep 30, 2026
42ae679
Log at info when an alert skips a disabled or filtered webhook
Damans227 Sep 30, 2026
9ec5487
Rename the volume size metric to volume used and add volume used percent
Damans227 Sep 30, 2026
91bb2c5
Show friendly labels for resource alert rule types, metrics, conditio…
Damans227 Sep 30, 2026
2811252
Leave system VM and router volumes out of all-volumes resource alert …
Damans227 Sep 30, 2026
0739a04
Fix bulk delete of resource alert rules and say the history goes too
Damans227 Sep 30, 2026
d86bfaa
Let resource alert rules be disabled and enabled again
Damans227 Sep 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ jobs:
smoke/test_nested_virtualization
smoke/test_set_sourcenat
smoke/test_webhook_lifecycle
smoke/test_resource_alerts
smoke/test_purge_expunged_vms
smoke/test_extension_lifecycle
smoke/test_extension_custom_action_lifecycle
Expand Down
5 changes: 5 additions & 0 deletions api/src/main/java/com/cloud/event/EventTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -822,6 +822,11 @@ public class EventTypes {
public static final String EVENT_QUOTA_TARIFF_DELETE = "QUOTA.TARIFF.DELETE";
public static final String EVENT_QUOTA_TARIFF_UPDATE = "QUOTA.TARIFF.UPDATE";

// Resource alert rules
public static final String EVENT_RESOURCE_ALERT_RULE_CREATE = "RESOURCE.ALERT.RULE.CREATE";
public static final String EVENT_RESOURCE_ALERT_RULE_UPDATE = "RESOURCE.ALERT.RULE.UPDATE";
public static final String EVENT_RESOURCE_ALERT_RULE_DELETE = "RESOURCE.ALERT.RULE.DELETE";

// Routing
public static final String EVENT_ZONE_IP4_SUBNET_CREATE = "ZONE.IP4.SUBNET.CREATE";
public static final String EVENT_ZONE_IP4_SUBNET_UPDATE = "ZONE.IP4.SUBNET.UPDATE";
Expand Down
8 changes: 8 additions & 0 deletions api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -1089,6 +1089,14 @@ public class ApiConstants {
public static final String AGGR_FUNCTION = "aggrfunction";
public static final String AGGR_VALUE = "aggrvalue";
public static final String THRESHOLD = "threshold";
public static final String METRIC = "metric";
public static final String CONDITION = "condition";
public static final String SEVERITY = "severity";
public static final String RESET_INTERVAL = "resetinterval";
public static final String WEBHOOK_IDS = "webhookids";
public static final String WEBHOOK_NAMES = "webhooknames";
public static final String CLEANUP_WEBHOOKS = "cleanupwebhooks";
public static final String ALERT_RULE_ID = "alertruleid";
public static final String RELATIONAL_OPERATOR = "relationaloperator";
public static final String OTHER_DEPLOY_PARAMS = "otherdeployparams";
public static final String MIN_MEMBERS = "minmembers";
Expand Down
5 changes: 5 additions & 0 deletions client/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,11 @@
<artifactId>cloud-mom-webhook</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloud-plugin-resource-alerts</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloud-framework-agent-lb</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ public interface VolumeDao extends GenericDao<VolumeVO, Long>, StateDao<Volume.S

List<VolumeVO> findByAccount(long accountId);

/**
* Lists IDs of volumes that are detached or attached to user VMs, leaving out system VM and router volumes.
*/
List<Long> listUserVolumeIdsByAccountOrDomainsAndState(Long accountId, List<Long> domainIds, Volume.State state);

List<VolumeVO> findIncludingRemovedByAccount(long accountId);

Pair<Long, Long> getCountAndTotalByPool(long poolId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol

private static final String ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT_PART1 = "SELECT pool.id, SUM(IF(vol.state='Ready' AND vol.account_id = ?, 1, 0)) FROM `cloud`.`storage_pool` pool LEFT JOIN `cloud`.`volumes` vol ON pool.id = vol.pool_id WHERE pool.data_center_id = ? ";
private static final String ORDER_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT_PART2 = " GROUP BY pool.id ORDER BY 2 ASC ";
private static final String LIST_USER_VOLUME_IDS = "SELECT vol.id FROM `cloud`.`volumes` vol "
+ "LEFT JOIN `cloud`.`vm_instance` vm ON vm.id = vol.instance_id "
+ "WHERE vol.removed IS NULL AND (vol.instance_id IS NULL OR vm.type = 'User')";

private static final String ORDER_ZONE_WIDE_POOLS_NUMBER_OF_VOLUMES_FOR_ACCOUNT = "SELECT pool.id, SUM(IF(vol.state='Ready' AND vol.account_id = ?, 1, 0)) FROM `cloud`.`storage_pool` pool LEFT JOIN `cloud`.`volumes` vol ON pool.id = vol.pool_id WHERE pool.data_center_id = ? "
+ " AND pool.scope = 'ZONE' AND pool.status='Up' " + " GROUP BY pool.id ORDER BY 2 ASC ";
Expand All @@ -118,6 +121,47 @@ public List<VolumeVO> findByAccount(long accountId) {
return listBy(sc);
}

@Override
public List<Long> listUserVolumeIdsByAccountOrDomainsAndState(Long accountId, List<Long> domainIds, Volume.State state) {
if (domainIds != null && domainIds.isEmpty()) {
return new ArrayList<>();
}
StringBuilder sql = new StringBuilder(LIST_USER_VOLUME_IDS);
if (accountId != null) {
sql.append(" AND vol.account_id = ?");
}
if (domainIds != null) {
sql.append(" AND vol.domain_id IN (").append(String.join(",", Collections.nCopies(domainIds.size(), "?"))).append(")");
}
if (state != null) {
sql.append(" AND vol.state = ?");
}
List<Long> ids = new ArrayList<>();
TransactionLegacy txn = TransactionLegacy.currentTxn();
try (PreparedStatement pstmt = txn.prepareAutoCloseStatement(sql.toString())) {
int i = 1;
if (accountId != null) {
pstmt.setLong(i++, accountId);
}
if (domainIds != null) {
for (Long domainId : domainIds) {
pstmt.setLong(i++, domainId);
}
}
if (state != null) {
pstmt.setString(i, state.name());
}
try (ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
ids.add(rs.getLong(1));
}
}
} catch (SQLException e) {
throw new CloudRuntimeException("Unable to list user volume IDs", e);
}
return ids;
}

@Override
public List<VolumeVO> findIncludingRemovedByAccount(long accountId) {
SearchCriteria<VolumeVO> sc = AllFieldsSearch.create();
Expand Down
2 changes: 2 additions & 0 deletions engine/schema/src/main/java/com/cloud/vm/dao/UserVmDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
public interface UserVmDao extends GenericDao<UserVmVO, Long> {
List<UserVmVO> listByAccountId(long id);

List<Long> listIdsByAccountOrDomainsAndState(Long accountId, List<Long> domainIds, State state);

List<UserVmVO> listByAccountAndPod(long accountId, long podId);

List<UserVmVO> listByAccountAndDataCenter(long accountId, long dcId);
Expand Down
23 changes: 23 additions & 0 deletions engine/schema/src/main/java/com/cloud/vm/dao/UserVmDaoImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ public class UserVmDaoImpl extends GenericDaoBase<UserVmVO, Long> implements Use
protected SearchBuilder<UserVmVO> AccountDataCenterVirtualSearch;
protected GenericSearchBuilder<UserVmVO, Long> CountByAccountPod;
protected GenericSearchBuilder<UserVmVO, Long> CountByAccount;
protected GenericSearchBuilder<UserVmVO, Long> IdsByAccountOrDomainsAndStateSearch;
protected GenericSearchBuilder<UserVmVO, Long> CountActiveAccount;
protected GenericSearchBuilder<UserVmVO, Long> PodsHavingVmsForAccount;

Expand Down Expand Up @@ -143,6 +144,13 @@ void init() {
AccountSearch.and("account", AccountSearch.entity().getAccountId(), SearchCriteria.Op.EQ);
AccountSearch.done();

IdsByAccountOrDomainsAndStateSearch = createSearchBuilder(Long.class);
IdsByAccountOrDomainsAndStateSearch.selectFields(IdsByAccountOrDomainsAndStateSearch.entity().getId());
IdsByAccountOrDomainsAndStateSearch.and("accountId", IdsByAccountOrDomainsAndStateSearch.entity().getAccountId(), SearchCriteria.Op.EQ);
IdsByAccountOrDomainsAndStateSearch.and("domainIds", IdsByAccountOrDomainsAndStateSearch.entity().getDomainId(), SearchCriteria.Op.IN);
IdsByAccountOrDomainsAndStateSearch.and("state", IdsByAccountOrDomainsAndStateSearch.entity().getState(), SearchCriteria.Op.EQ);
IdsByAccountOrDomainsAndStateSearch.done();

IdsSearch = createSearchBuilder();
IdsSearch.and("ids", IdsSearch.entity().getId(), SearchCriteria.Op.IN);
IdsSearch.done();
Expand Down Expand Up @@ -318,6 +326,21 @@ public List<UserVmVO> listByAccountId(long id) {
return listBy(sc);
}

@Override
public List<Long> listIdsByAccountOrDomainsAndState(Long accountId, List<Long> domainIds, State state) {
SearchCriteria<Long> sc = IdsByAccountOrDomainsAndStateSearch.create();
if (accountId != null) {
sc.setParameters("accountId", accountId);
}
if (domainIds != null) {
sc.setParameters("domainIds", domainIds.toArray());
}
if (state != null) {
sc.setParameters("state", state);
}
return customSearch(sc, null);
}

@Override
public List<UserVmVO> listByHostId(Long id) {
SearchCriteria<UserVmVO> sc = HostSearch.create();
Expand Down
7 changes: 7 additions & 0 deletions engine/schema/src/main/java/com/cloud/vm/dao/VmStatsDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ public interface VmStatsDao extends GenericDao<VmStatsVO, Long> {
*/
List<VmStatsVO> findByVmIdOrderByTimestampDesc(long vmId);

/**
* Finds the newest stats of a VM.
* @param vmId the VM ID.
* @return the newest stats, or null if there are none.
*/
VmStatsVO findLatestByVmId(long vmId);

/**
* Finds stats by VM ID and timestamp >= a given time.
* @param vmId the specific VM.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,15 @@ public List<VmStatsVO> findByVmIdOrderByTimestampDesc(long vmId) {
return search(sc, orderByFilter, null, false);
}

@Override
public VmStatsVO findLatestByVmId(long vmId) {
SearchCriteria<VmStatsVO> sc = vmIdSearch.create();
sc.setParameters("vmId", vmId);
Filter orderByFilter = new Filter(VmStatsVO.class, "timestamp", false, 0L, 1L);
List<VmStatsVO> stats = search(sc, orderByFilter, null, false);
return stats.isEmpty() ? null : stats.get(0);
}

@Override
public List<VmStatsVO> findByVmIdAndTimestampGreaterThanEqual(long vmId, Date time) {
SearchCriteria<VmStatsVO> sc = vmIdTimestampGreaterThanEqualSearch.create();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,64 @@
--;
-- Schema upgrade from 4.23.0.0 to 24.0.0
--;

-- resource_alert_rules: stores per-resource or generic metric threshold rules
CREATE TABLE IF NOT EXISTS `cloud`.`resource_alert_rules` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`uuid` varchar(255) NOT NULL UNIQUE,
`name` varchar(255) NOT NULL,
`resource_type` varchar(64) NOT NULL COMMENT 'VirtualMachine, Volume, Host, StoragePool',
`resource_id` bigint unsigned DEFAULT NULL COMMENT 'null = applies to all resources of the type in scope',
`account_id` bigint unsigned NOT NULL,
`domain_id` bigint unsigned NOT NULL,
`metric` varchar(64) NOT NULL,
`condition_operator` varchar(8) NOT NULL COMMENT 'GT, GTE, LT, LTE, EQ',
`threshold` double NOT NULL,
`severity` varchar(32) NOT NULL COMMENT 'CRITICAL, HIGH, MEDIUM, LOW',
`message` varchar(4096) DEFAULT NULL,
`email` tinyint(1) NOT NULL DEFAULT 0,
`reset_interval` int unsigned NOT NULL DEFAULT 600 COMMENT 'minimum seconds between repeat firings of this rule',
`state` varchar(32) NOT NULL DEFAULT 'Enabled' COMMENT 'Enabled or Disabled; disabled rules are not checked',
`created` datetime DEFAULT NULL,
`updated` datetime DEFAULT NULL,
`removed` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
INDEX `i_resource_alert_rules__account_id`(`account_id`),
INDEX `i_resource_alert_rules__domain_id`(`domain_id`),
INDEX `i_resource_alert_rules__resource_type__resource_id`(`resource_type`, `resource_id`),
CONSTRAINT `fk_resource_alert_rules__account_id` FOREIGN KEY (`account_id`) REFERENCES `account`(`id`) ON DELETE CASCADE,
CONSTRAINT `fk_resource_alert_rules__domain_id` FOREIGN KEY (`domain_id`) REFERENCES `domain`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- resource_alerts: immutable log of fired alerts
CREATE TABLE IF NOT EXISTS `cloud`.`resource_alerts` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`uuid` varchar(255) NOT NULL UNIQUE,
`alert_rule_id` bigint unsigned NOT NULL,
`resource_id` bigint unsigned DEFAULT NULL COMMENT 'the specific resource that triggered the alert',
`metric_type` varchar(64) NOT NULL,
`metric_value` double NOT NULL,
`severity` varchar(32) NOT NULL,
`message` varchar(4096) DEFAULT NULL,
`alert_timestamp` datetime NOT NULL,
PRIMARY KEY (`id`),
INDEX `i_resource_alerts__alert_rule_id__resource_id__alert_timestamp`(`alert_rule_id`, `resource_id`, `alert_timestamp`),
INDEX `i_resource_alerts__resource_id__alert_timestamp`(`resource_id`, `alert_timestamp`),
INDEX `i_resource_alerts__alert_timestamp`(`alert_timestamp`),
CONSTRAINT `fk_resource_alerts__alert_rule_id` FOREIGN KEY (`alert_rule_id`) REFERENCES `resource_alert_rules`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- resource_alert_rules_webhook: webhooks a rule delivers its alerts to
CREATE TABLE IF NOT EXISTS `cloud`.`resource_alert_rules_webhook` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`resource_alert_rule_id` bigint unsigned NOT NULL,
`webhook_id` bigint unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uc_resource_alert_rules_webhook__rule_webhook`(`resource_alert_rule_id`, `webhook_id`),
CONSTRAINT `fk_resource_alert_rules_webhook__rule_id` FOREIGN KEY (`resource_alert_rule_id`) REFERENCES `resource_alert_rules`(`id`) ON DELETE CASCADE,
CONSTRAINT `fk_resource_alert_rules_webhook__webhook_id` FOREIGN KEY (`webhook_id`) REFERENCES `webhook`(`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- webhook_delivery: allow deliveries that are not tied to a stored event, such as resource alerts
CALL `cloud`.`IDEMPOTENT_CHANGE_COLUMN`('cloud.webhook_delivery', 'event_id', 'event_id', 'bigint unsigned COMMENT "id of the event"');
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.webhook_delivery', 'event_type', 'varchar(255) COMMENT "type of the event when the delivery is not tied to a stored event"');
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
-- Licensed to the Apache Software Foundation (ASF) under one
-- or more contributor license agreements. See the NOTICE file
-- distributed with this work for additional information
-- regarding copyright ownership. The ASF licenses this file
-- to you under the Apache License, Version 2.0 (the
-- "License"); you may not use this file except in compliance
-- with the License. You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing,
-- software distributed under the License is distributed on an
-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-- KIND, either express or implied. See the License for the
-- specific language governing permissions and limitations
-- under the License.

-- VIEW `cloud`.`resource_alert_rule_view`;

DROP VIEW IF EXISTS `cloud`.`resource_alert_rule_view`;
CREATE VIEW `cloud`.`resource_alert_rule_view` AS
SELECT
r.id,
r.uuid,
r.name,
r.resource_type,
r.resource_id,
r.metric,
r.condition_operator,
r.threshold,
r.severity,
r.message,
r.email,
r.reset_interval,
r.state,
r.created,
r.updated,
r.removed,
a.id account_id,
a.uuid account_uuid,
a.account_name,
a.type account_type,
d.id domain_id,
d.uuid domain_uuid,
d.name domain_name,
d.path domain_path,
p.uuid project_uuid,
p.name project_name
FROM `cloud`.`resource_alert_rules` r
INNER JOIN `cloud`.`account` a ON r.account_id = a.id
INNER JOIN `cloud`.`domain` d ON r.domain_id = d.id
LEFT JOIN `cloud`.`projects` p ON p.project_account_id = a.id;
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ CREATE VIEW `cloud`.`webhook_delivery_view` AS
webhook_delivery.response,
webhook_delivery.start_time,
webhook_delivery.end_time,
event.id event_id,
webhook_delivery.event_id,
event.uuid event_uuid,
event.type event_type,
IFNULL(event.type, webhook_delivery.event_type) event_type,
webhook.id webhook_id,
webhook.uuid webhook_uuid,
webhook.name webhook_name,
Expand All @@ -40,7 +40,7 @@ CREATE VIEW `cloud`.`webhook_delivery_view` AS
mshost.name mshost_name
FROM
`cloud`.`webhook_delivery`
INNER JOIN
LEFT JOIN
`cloud`.`event` ON webhook_delivery.event_id = event.id
INNER JOIN
`cloud`.`webhook` ON webhook_delivery.webhook_id = webhook.id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,32 @@ public void testListPoolIdsByVolumeCount_without_cluster_details() throws SQLExc
verify(preparedStatementMock, times(1)).executeQuery();
}

@Test
public void listUserVolumeIdsByAccountOrDomainsAndStateSkipsSystemVmVolumes() throws SQLException {
final String expectedSql = "SELECT vol.id FROM `cloud`.`volumes` vol "
+ "LEFT JOIN `cloud`.`vm_instance` vm ON vm.id = vol.instance_id "
+ "WHERE vol.removed IS NULL AND (vol.instance_id IS NULL OR vm.type = 'User')"
+ " AND vol.domain_id IN (?,?) AND vol.state = ?";
when(TransactionLegacy.currentTxn()).thenReturn(transactionMock);
when(transactionMock.prepareAutoCloseStatement(expectedSql)).thenReturn(preparedStatementMock);
ResultSet rs = Mockito.mock(ResultSet.class);
when(rs.next()).thenReturn(true, false);
when(rs.getLong(1)).thenReturn(7L);
when(preparedStatementMock.executeQuery()).thenReturn(rs);

List<Long> ids = volumeDao.listUserVolumeIdsByAccountOrDomainsAndState(null, List.of(1L, 2L), Volume.State.Ready);

Assert.assertEquals(List.of(7L), ids);
verify(preparedStatementMock).setLong(1, 1L);
verify(preparedStatementMock).setLong(2, 2L);
verify(preparedStatementMock).setString(3, "Ready");
}

@Test
public void listUserVolumeIdsByAccountOrDomainsAndStateWithNoDomains() {
Assert.assertTrue(volumeDao.listUserVolumeIdsByAccountOrDomainsAndState(null, List.of(), Volume.State.Ready).isEmpty());
}

@Test
public void findByInstanceAndNotState_queriesWithInstanceIdAndExcludedStates() {
SearchBuilder<VolumeVO> sb = Mockito.mock(SearchBuilder.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ public interface WebhookDelivery extends Identity, InternalIdentity {
public static final String TEST_EVENT_TYPE = "TEST.WEBHOOK";

long getId();
long getEventId();
Long getEventId();
String getEventType();
long getWebhookId();
long getManagementServerId();
String getHeaders();
Expand Down
Loading
Loading