diff --git a/docs/_docs/events/listening-to-events.adoc b/docs/_docs/events/listening-to-events.adoc index ea61c35012eca..d1db3cf337f9e 100644 --- a/docs/_docs/events/listening-to-events.adoc +++ b/docs/_docs/events/listening-to-events.adoc @@ -22,7 +22,7 @@ Ignite can generate events for a variety of operations happening in the cluster The list of events is available in the link:events/events[Events] section. == Enabling Events -By default, events are disabled, and you have to enable each event type explicitly if you want to use it in your application. +By default, almost all events are disabled (except for a few that are implicitly enabled because they are required by the system), and you have to enable each event type explicitly if you want to use it in your application. To enable specific event types, list them in the `includeEventTypes` property of `IgniteConfiguration` as shown below: [tabs] @@ -45,6 +45,8 @@ include::code-snippets/dotnet/WorkingWithEvents.cs[tag=enablingEvents,indent=0] tab:C++[unsupported] -- +Events can also be enabled/disabled in runtime by the link:tools/control-script[control utility]. + == Getting the Events Interface The events functionality is available through the events interface, which provides methods for listening to cluster events. The events interface can be obtained from an instance of `Ignite` as follows: diff --git a/docs/_docs/tools/control-script.adoc b/docs/_docs/tools/control-script.adoc index ba5c5b0924c86..1097921ba6b5f 100644 --- a/docs/_docs/tools/control-script.adoc +++ b/docs/_docs/tools/control-script.adoc @@ -1628,3 +1628,55 @@ Parameters: |--node-id | A list of nodes to rebuild indexes on. If not specified, schedules rebuild on all nodes. |--cache-names | Comma-separated list of cache names, optionally with indexes. If indexes are not specified, all indexes of the cache will be scheduled for the rebuild operation. Can be used simultaneously with cache group names. |--group-names | Comma-separated list of cache group names. Can be used simultaneously with cache names. + + +== Event management + +The control utility provides commands to enable, disable and check the status of events on all server nodes. + +=== Enable/disable events + +To enable or disable events on all server nodes, use the `--event enable/disable` command. + +[source, shell] +---- +control.sh|bat --event enable|disable event1[,...,eventN] +---- + +Parameters: + +[cols="1,3",opts="header"] +|=== +| Parameter | Description +| `event1[,...,eventN]`| Specifies a comma-separated list of events to enable/disable. +|=== + +Examples: +[source, shell] +---- +control.sh|bat --event enable EVT_CACHE_STARTED,EVT_CACHE_STOPPED +control.sh|bat --event disable EVT_CACHE_STARTED,EVT_CACHE_STOPPED +---- + +=== Events list and status + +To get the list and status of available events, use the `--event list` command. + +[source, shell] +---- +control.sh|bat --event list [--enabled] +---- + +Parameters: + +[cols="1,3",opts="header"] +|=== +| Parameter | Description +| `--enabled`| List only enabled evente. +|=== + +Examples: +[source, shell] +---- +control.sh|bat --event list --enabled +---- diff --git a/modules/control-utility/src/test/java/org/apache/ignite/internal/commandline/CommandHandlerParsingTest.java b/modules/control-utility/src/test/java/org/apache/ignite/internal/commandline/CommandHandlerParsingTest.java index 507d3bed7060f..a238e5862a968 100644 --- a/modules/control-utility/src/test/java/org/apache/ignite/internal/commandline/CommandHandlerParsingTest.java +++ b/modules/control-utility/src/test/java/org/apache/ignite/internal/commandline/CommandHandlerParsingTest.java @@ -69,6 +69,7 @@ import org.apache.ignite.internal.management.encryption.EncryptionChangeCacheKeyCommand; import org.apache.ignite.internal.management.encryption.EncryptionChangeMasterKeyCommand; import org.apache.ignite.internal.management.encryption.EncryptionCommand; +import org.apache.ignite.internal.management.event.EventCommand; import org.apache.ignite.internal.management.kill.KillCommand; import org.apache.ignite.internal.management.meta.MetaCommand; import org.apache.ignite.internal.management.meta.MetaRemoveCommand; @@ -1401,6 +1402,7 @@ private boolean requireArgs(Class cmd) { cmd == DefragmentationCommand.class || cmd == PerformanceStatisticsCommand.class || cmd == ConsistencyCommand.class || - cmd == CdcCommand.class; + cmd == CdcCommand.class || + cmd == EventCommand.class; } } diff --git a/modules/control-utility/src/test/java/org/apache/ignite/testsuites/IgniteControlUtilityTestSuite2.java b/modules/control-utility/src/test/java/org/apache/ignite/testsuites/IgniteControlUtilityTestSuite2.java index 0799c5c4110fb..c0adb33e5b2a9 100644 --- a/modules/control-utility/src/test/java/org/apache/ignite/testsuites/IgniteControlUtilityTestSuite2.java +++ b/modules/control-utility/src/test/java/org/apache/ignite/testsuites/IgniteControlUtilityTestSuite2.java @@ -26,6 +26,7 @@ import org.apache.ignite.util.GridCommandHandlerConsistencySensitiveTest; import org.apache.ignite.util.GridCommandHandlerConsistencyTest; import org.apache.ignite.util.GridCommandHandlerDefragmentationTest; +import org.apache.ignite.util.GridCommandHandlerEventTest; import org.apache.ignite.util.GridCommandHandlerIndexForceRebuildTest; import org.apache.ignite.util.GridCommandHandlerIndexListTest; import org.apache.ignite.util.GridCommandHandlerIndexRebuildStatusTest; @@ -60,6 +61,8 @@ GridCommandHandlerConsistencyBinaryTest.class, GridCommandHandlerConsistencySensitiveTest.class, + GridCommandHandlerEventTest.class, + RollingUpgradeCommandTest.class, SystemViewCommandTest.class, MetricCommandTest.class, diff --git a/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerEventTest.java b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerEventTest.java new file mode 100644 index 0000000000000..8290faab78ebf --- /dev/null +++ b/modules/control-utility/src/test/java/org/apache/ignite/util/GridCommandHandlerEventTest.java @@ -0,0 +1,206 @@ +/* + * 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. + */ + +package org.apache.ignite.util; + +import java.security.Permissions; +import org.apache.ignite.Ignite; +import org.apache.ignite.configuration.IgniteConfiguration; +import org.apache.ignite.events.EventType; +import org.apache.ignite.internal.processors.security.impl.TestSecurityData; +import org.apache.ignite.internal.processors.security.impl.TestSecurityPluginProvider; +import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.G; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.junit.Test; + +import static org.apache.ignite.internal.commandline.CommandHandler.EXIT_CODE_OK; +import static org.apache.ignite.internal.commandline.CommandHandler.EXIT_CODE_UNEXPECTED_ERROR; +import static org.apache.ignite.internal.commandline.SecurityCommandHandlerPermissionsTest.DEFAULT_PWD; +import static org.apache.ignite.internal.commandline.SecurityCommandHandlerPermissionsTest.TEST_NO_PERMISSIONS_LOGIN; +import static org.apache.ignite.internal.commandline.SecurityCommandHandlerPermissionsTest.enrichWithConnectionArguments; +import static org.apache.ignite.plugin.security.SecurityPermission.ADMIN_OPS; +import static org.apache.ignite.plugin.security.SecurityPermission.EVENTS_DISABLE; +import static org.apache.ignite.plugin.security.SecurityPermission.EVENTS_ENABLE; +import static org.apache.ignite.plugin.security.SecurityPermissionSetBuilder.ALL_PERMISSIONS; +import static org.apache.ignite.plugin.security.SecurityPermissionSetBuilder.NO_PERMISSIONS; +import static org.apache.ignite.plugin.security.SecurityPermissionSetBuilder.systemPermissions; +import static org.junit.Assume.assumeTrue; + +/** */ +public class GridCommandHandlerEventTest extends GridCommandHandlerAbstractTest { + /** */ + private static final String LOGIN_EVT_ENABLE = "test_login_evt_enable"; + + /** */ + private static final String LOGIN_EVT_DISABLE = "test_login_evt_disable"; + + /** */ + private static final String LOGIN_EVT_STATUS = "test_login_evt_status"; + + /** */ + @Override protected void afterTest() throws Exception { + super.afterTest(); + + stopAllGrids(); + } + + /** */ + @Test + public void testEventEnableDIsable() throws Exception { + startGrids(2); + startClientGrid("client"); + + assertEvents(false, EventType.EVT_CACHE_STARTED, EventType.EVT_CACHE_STOPPED); + + assertEquals(EXIT_CODE_OK, execute("--event", "enable", "EVT_CACHE_STARTED,EVT_CACHE_STOPPED")); + + assertEvents(true, EventType.EVT_CACHE_STARTED, EventType.EVT_CACHE_STOPPED); + + injectTestSystemOut(); + + assertEquals(EXIT_CODE_OK, execute("--event", "list", "--enabled")); + + String out = testOut.toString(); + + assertTrue(out.contains("EVT_CACHE_STARTED")); + assertTrue(out.contains("EVT_CACHE_STOPPED")); + + assertEquals(EXIT_CODE_OK, execute("--event", "disable", "EVT_CACHE_STARTED,EVT_CACHE_STOPPED")); + + assertEvents(false, EventType.EVT_CACHE_STARTED, EventType.EVT_CACHE_STOPPED); + } + + /** */ + @Test + public void testDifferentSetOfEventsOnNodes() throws Exception { + startGrid(getConfiguration(getTestIgniteInstanceName(0)) + .setIncludeEventTypes(EventType.EVT_TX_STARTED, EventType.EVT_CACHE_STARTED)); + + startGrid(getConfiguration(getTestIgniteInstanceName(1)) + .setIncludeEventTypes(EventType.EVT_TX_STARTED, EventType.EVT_CACHE_STOPPED)); + + assertEvents(true, EventType.EVT_TX_STARTED, EventType.EVT_CACHE_STARTED); + assertEvents(false, EventType.EVT_CACHE_STOPPED); + + injectTestSystemOut(); + + assertEquals(EXIT_CODE_OK, execute("--event", "list", "--enabled")); + + String out = testOut.toString(); + + assertTrue(out.contains("EVT_TX_STARTED")); + assertTrue(out.contains("EVT_CACHE_STARTED")); + assertFalse(out.contains("EVT_CACHE_STOPPED")); + + assertEquals(EXIT_CODE_OK, execute("--event", "disable", "EVT_TX_STARTED")); + + startGrid(getConfiguration(getTestIgniteInstanceName(2)) + .setIncludeEventTypes(EventType.EVT_TX_STARTED, EventType.EVT_CACHE_STOPPED)); + + assertEvents(true, EventType.EVT_CACHE_STARTED); + assertEvents(false, EventType.EVT_TX_STARTED, EventType.EVT_CACHE_STOPPED); + + assertEquals(EXIT_CODE_OK, execute("--event", "list", "--enabled")); + + out = testOut.toString(); + + assertFalse(out.contains("EVT_TX_STARTED")); + assertTrue(out.contains("EVT_CACHE_STARTED")); + assertFalse(out.contains("EVT_CACHE_STOPPED")); + } + + /** */ + @Test + public void testUnknownEvent() throws Exception { + startGrids(2); + + assertEquals(EXIT_CODE_UNEXPECTED_ERROR, execute("--event", "enable", "EVT_NO_SUCH_EVENT")); + } + + /** */ + @Test + public void testEventList() throws Exception { + startGrids(2); + + injectTestSystemOut(); + + assertEquals(EXIT_CODE_OK, execute("--event", "list")); + + String out = testOut.toString(); + + for (String evt : U.gridEventNames().values()) + assertTrue(out.contains("EVT_" + evt)); + } + + /** */ + @Test + public void testEventWithSecurity() throws Exception { + assumeTrue(cliCommandHandler()); + + startGrid(getConfigurationWithSecurity(getTestIgniteInstanceName(0))); + startGrid(getConfigurationWithSecurity(getTestIgniteInstanceName(1))); + + assertExecuteWithSecurity(LOGIN_EVT_ENABLE, "--event", "enable", "EVT_CACHE_STARTED,EVT_CACHE_STOPPED"); + + assertEvents(true, EventType.EVT_CACHE_STARTED, EventType.EVT_CACHE_STOPPED); + + assertExecuteWithSecurity(LOGIN_EVT_STATUS, "--event", "list"); + + assertExecuteWithSecurity(LOGIN_EVT_DISABLE, "--event", "disable", "EVT_CACHE_STARTED,EVT_CACHE_STOPPED"); + + assertEvents(false, EventType.EVT_CACHE_STARTED, EventType.EVT_CACHE_STOPPED); + } + + /** */ + private IgniteConfiguration getConfigurationWithSecurity(String igniteInstanceName) throws Exception { + IgniteConfiguration cfg = getConfiguration(igniteInstanceName); + + cfg.setPluginProviders( + new TestSecurityPluginProvider( + igniteInstanceName, + DEFAULT_PWD, + ALL_PERMISSIONS, + false, + new TestSecurityData(TEST_NO_PERMISSIONS_LOGIN, DEFAULT_PWD, NO_PERMISSIONS, new Permissions()), + new TestSecurityData(LOGIN_EVT_ENABLE, DEFAULT_PWD, systemPermissions(EVENTS_ENABLE), new Permissions()), + new TestSecurityData(LOGIN_EVT_DISABLE, DEFAULT_PWD, systemPermissions(EVENTS_DISABLE), new Permissions()), + new TestSecurityData(LOGIN_EVT_STATUS, DEFAULT_PWD, systemPermissions(ADMIN_OPS), new Permissions())) + ); + + return cfg; + } + + /** */ + private void assertExecuteWithSecurity(String allowedLogin, String... cmdArgs) { + assertEquals(EXIT_CODE_UNEXPECTED_ERROR, execute(enrichWithConnectionArguments(F.asList(cmdArgs), + TEST_NO_PERMISSIONS_LOGIN))); + + assertEquals(EXIT_CODE_OK, execute(enrichWithConnectionArguments(F.asList(cmdArgs), allowedLogin))); + } + + /** */ + private void assertEvents(boolean enabled, int... evts) { + for (Ignite ignite : G.allGrids()) { + if (ignite.configuration().isClientMode()) + continue; + + for (int i = 0; i < evts.length; i++) + assertEquals(enabled, ignite.events().isEnabled(evts[i])); + } + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/GridComponent.java b/modules/core/src/main/java/org/apache/ignite/internal/GridComponent.java index 60198749dfdad..87d769bf3d7de 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/GridComponent.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/GridComponent.java @@ -79,7 +79,10 @@ enum DiscoveryDataExchangeType { META_STORAGE, /** Performance statistics processor. */ - PERFORMANCE_STAT_PROC; + PERFORMANCE_STAT_PROC, + + /** Event storage manager. */ + EVENT_MGR; /** Cached values array. */ public static final DiscoveryDataExchangeType[] VALUES = values(); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/IgniteCommandRegistry.java b/modules/core/src/main/java/org/apache/ignite/internal/management/IgniteCommandRegistry.java index 1f28f1cfbd24c..9e6b2194ded16 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/management/IgniteCommandRegistry.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/management/IgniteCommandRegistry.java @@ -29,6 +29,7 @@ import org.apache.ignite.internal.management.defragmentation.DefragmentationCommand; import org.apache.ignite.internal.management.diagnostic.DiagnosticCommand; import org.apache.ignite.internal.management.encryption.EncryptionCommand; +import org.apache.ignite.internal.management.event.EventCommand; import org.apache.ignite.internal.management.kill.KillCommand; import org.apache.ignite.internal.management.meta.MetaCommand; import org.apache.ignite.internal.management.metric.MetricCommand; @@ -77,7 +78,8 @@ public IgniteCommandRegistry() { new DefragmentationCommand(), new PerformanceStatisticsCommand(), new CdcCommand(), - new ConsistencyCommand() + new ConsistencyCommand(), + new EventCommand() ); U.loadService(CommandsProvider.class).forEach(p -> p.commands().forEach(this::register)); diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/event/EventCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/event/EventCommand.java new file mode 100644 index 0000000000000..fadc300788bb6 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/management/event/EventCommand.java @@ -0,0 +1,33 @@ +/* + * 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. + */ + +package org.apache.ignite.internal.management.event; + +import org.apache.ignite.internal.management.api.CommandRegistryImpl; +import org.apache.ignite.internal.management.api.NoArg; + +/** Command to enable/disable events. */ +public class EventCommand extends CommandRegistryImpl { + /** */ + public EventCommand() { + super( + new EventEnableCommand(), + new EventDisableCommand(), + new EventListCommand() + ); + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/event/EventCommandArg.java b/modules/core/src/main/java/org/apache/ignite/internal/management/event/EventCommandArg.java new file mode 100644 index 0000000000000..000386c0a098d --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/management/event/EventCommandArg.java @@ -0,0 +1,47 @@ +/* + * 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. + */ + +package org.apache.ignite.internal.management.event; + +import org.apache.ignite.internal.Order; +import org.apache.ignite.internal.dto.IgniteDataTransferObject; +import org.apache.ignite.internal.management.api.Argument; +import org.apache.ignite.internal.management.api.Positional; + +/** */ +public class EventCommandArg extends IgniteDataTransferObject { + /** */ + private static final long serialVersionUID = 0; + + /** */ + @Order(0) + @Positional + @Argument( + description = "Comma separated list of events", + example = "event1[,...,eventN]") + String[] events; + + /** */ + public String[] events() { + return events; + } + + /** */ + public void events(String[] events) { + this.events = events; + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/event/EventDisableCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/event/EventDisableCommand.java new file mode 100644 index 0000000000000..8ece6cb6c7070 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/management/event/EventDisableCommand.java @@ -0,0 +1,59 @@ +/* + * 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. + */ + +package org.apache.ignite.internal.management.event; + +import java.util.Collection; +import java.util.function.Consumer; +import org.apache.ignite.cluster.ClusterNode; +import org.apache.ignite.internal.management.api.ComputeCommand; + +import static org.apache.ignite.internal.management.api.CommandUtils.servers; + +/** */ +public class EventDisableCommand implements ComputeCommand { + /** {@inheritDoc} */ + @Override public String description() { + return "Disable events on all server nodes"; + } + + /** {@inheritDoc} */ + @Override public Class argClass() { + return EventDisableCommandArg.class; + } + + /** {@inheritDoc} */ + @Override public Class taskClass() { + return EventManagementTask.class; + } + + /** {@inheritDoc} */ + @Override public Collection nodes(Collection nodes, EventCommandArg arg) { + return servers(nodes); + } + + /** {@inheritDoc} */ + @Override public void printResult(EventCommandArg arg, Void res, Consumer printer) { + printer.accept("Events were disabled [evts=" + String.join(", ", arg.events()) + ']'); + } + + /** */ + public static class EventDisableCommandArg extends EventCommandArg { + /** */ + private static final long serialVersionUID = 0; + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/event/EventEnableCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/event/EventEnableCommand.java new file mode 100644 index 0000000000000..fd301821dfc9d --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/management/event/EventEnableCommand.java @@ -0,0 +1,59 @@ +/* + * 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. + */ + +package org.apache.ignite.internal.management.event; + +import java.util.Collection; +import java.util.function.Consumer; +import org.apache.ignite.cluster.ClusterNode; +import org.apache.ignite.internal.management.api.ComputeCommand; + +import static org.apache.ignite.internal.management.api.CommandUtils.servers; + +/** */ +public class EventEnableCommand implements ComputeCommand { + /** {@inheritDoc} */ + @Override public String description() { + return "Enable events on all server nodes"; + } + + /** {@inheritDoc} */ + @Override public Class argClass() { + return EventEnableCommandArg.class; + } + + /** {@inheritDoc} */ + @Override public Class taskClass() { + return EventManagementTask.class; + } + + /** {@inheritDoc} */ + @Override public Collection nodes(Collection nodes, EventCommandArg arg) { + return servers(nodes); + } + + /** {@inheritDoc} */ + @Override public void printResult(EventCommandArg arg, Void res, Consumer printer) { + printer.accept("Events were enabled [evts=" + String.join(", ", arg.events()) + ']'); + } + + /** */ + public static class EventEnableCommandArg extends EventCommandArg { + /** */ + private static final long serialVersionUID = 0; + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/event/EventListCommand.java b/modules/core/src/main/java/org/apache/ignite/internal/management/event/EventListCommand.java new file mode 100644 index 0000000000000..2165ac617ea73 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/management/event/EventListCommand.java @@ -0,0 +1,63 @@ +/* + * 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. + */ + +package org.apache.ignite.internal.management.event; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import org.apache.ignite.cluster.ClusterNode; +import org.apache.ignite.internal.management.SystemViewCommand; +import org.apache.ignite.internal.management.api.ComputeCommand; + +import static org.apache.ignite.internal.management.SystemViewTask.SimpleType.STRING; +import static org.apache.ignite.internal.management.api.CommandUtils.servers; + +/** */ +public class EventListCommand implements ComputeCommand> { + /** {@inheritDoc} */ + @Override public String description() { + return "List and status of events"; + } + + /** {@inheritDoc} */ + @Override public Class argClass() { + return EventListCommandArg.class; + } + + /** {@inheritDoc} */ + @Override public Class taskClass() { + return EventListTask.class; + } + + /** {@inheritDoc} */ + @Override public Collection nodes(Collection nodes, EventListCommandArg arg) { + return servers(nodes); + } + + /** {@inheritDoc} */ + @Override public void printResult(EventListCommandArg arg, Map res, Consumer printer) { + SystemViewCommand.printTable( + List.of("Event", "Status"), + List.of(STRING, STRING), + res.entrySet().stream().map(e -> List.of(e.getKey(), e.getValue())).collect(Collectors.toList()), + printer + ); + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/event/EventListCommandArg.java b/modules/core/src/main/java/org/apache/ignite/internal/management/event/EventListCommandArg.java new file mode 100644 index 0000000000000..6447570891be5 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/management/event/EventListCommandArg.java @@ -0,0 +1,45 @@ +/* + * 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. + */ + +package org.apache.ignite.internal.management.event; + +import org.apache.ignite.internal.Order; +import org.apache.ignite.internal.dto.IgniteDataTransferObject; +import org.apache.ignite.internal.management.api.Argument; + +/** */ +public class EventListCommandArg extends IgniteDataTransferObject { + /** */ + private static final long serialVersionUID = 0; + + /** */ + @Order(0) + @Argument( + optional = true, + description = "Filter only enabled events") + boolean enabled; + + /** */ + public boolean enabled() { + return enabled; + } + + /** */ + public void enabled(boolean enabled) { + this.enabled = enabled; + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/event/EventListTask.java b/modules/core/src/main/java/org/apache/ignite/internal/management/event/EventListTask.java new file mode 100644 index 0000000000000..2e462a2602224 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/management/event/EventListTask.java @@ -0,0 +1,103 @@ +/* + * 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. + */ + +package org.apache.ignite.internal.management.event; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import org.apache.ignite.IgniteException; +import org.apache.ignite.compute.ComputeJobResult; +import org.apache.ignite.internal.processors.task.GridInternal; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.ignite.internal.visor.VisorJob; +import org.apache.ignite.internal.visor.VisorMultiNodeTask; +import org.jetbrains.annotations.Nullable; + +/** + * Events status task. + */ +@GridInternal +public class EventListTask extends VisorMultiNodeTask, Collection> { + /** Serial version uid. */ + private static final long serialVersionUID = 0L; + + /** {@inheritDoc} */ + @Override protected VisorJob> job(EventListCommandArg arg) { + return new EventStatusJob(arg, debug); + } + + /** {@inheritDoc} */ + @Nullable @Override protected Map reduce0(List results) { + for (ComputeJobResult res : results) { + if (res.getException() != null) + throw res.getException(); + } + + Map res = new TreeMap<>(); + + ((Iterable)results.get(0).getData()).forEach(evt -> res.put(evt, "Enabled")); + + for (int i = 1; i < results.size(); i++) { + Collection res0 = results.get(i).getData(); + + res.keySet().retainAll(res0); + } + + for (int i = 0; i < results.size(); i++) { + Collection res0 = results.get(i).getData(); + + for (String evtName : res0) { + if (!res.containsKey(evtName)) + res.put(evtName, "Enabled/Disabled (not consistent across cluster)"); + } + } + + if (!taskArg.enabled()) + U.gridEventNames().values().forEach(e -> res.putIfAbsent("EVT_" + e, "Disabled")); + + return res; + } + + /** The job for view events status. */ + private static class EventStatusJob extends VisorJob> { + /** Serial version uid. */ + private static final long serialVersionUID = 0L; + + /** + * @param arg Job argument. + * @param debug Flag indicating whether debug information should be printed into node log. + */ + protected EventStatusJob(EventListCommandArg arg, boolean debug) { + super(arg, debug); + } + + /** {@inheritDoc} */ + @Override protected Collection run(EventListCommandArg arg) throws IgniteException { + int[] evts = ignite.context().event().enabledEvents(); + + Collection res = new ArrayList<>(); + + for (int i = 0; i < evts.length; i++) + res.add("EVT_" + U.gridEventName(evts[i])); + + return res; + } + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/management/event/EventManagementTask.java b/modules/core/src/main/java/org/apache/ignite/internal/management/event/EventManagementTask.java new file mode 100644 index 0000000000000..c267b2b1c4cf5 --- /dev/null +++ b/modules/core/src/main/java/org/apache/ignite/internal/management/event/EventManagementTask.java @@ -0,0 +1,105 @@ +/* + * 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. + */ + +package org.apache.ignite.internal.management.event; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import org.apache.ignite.IgniteException; +import org.apache.ignite.compute.ComputeJobResult; +import org.apache.ignite.internal.processors.task.GridInternal; +import org.apache.ignite.internal.util.typedef.internal.U; +import org.apache.ignite.internal.visor.VisorJob; +import org.apache.ignite.internal.visor.VisorMultiNodeTask; +import org.apache.ignite.plugin.security.SecurityPermissionSet; +import org.jetbrains.annotations.Nullable; + +import static org.apache.ignite.plugin.security.SecurityPermission.EVENTS_DISABLE; +import static org.apache.ignite.plugin.security.SecurityPermission.EVENTS_ENABLE; +import static org.apache.ignite.plugin.security.SecurityPermissionSetBuilder.systemPermissions; + +/** + * Enable/disable events task. + */ +@GridInternal +public class EventManagementTask extends VisorMultiNodeTask { + /** Serial version uid. */ + private static final long serialVersionUID = 0L; + + /** {@inheritDoc} */ + @Override protected VisorJob job(EventCommandArg arg) { + return new EventManagementJob(arg, debug); + } + + /** {@inheritDoc} */ + @Nullable @Override protected Void reduce0(List results) { + for (ComputeJobResult res : results) { + if (res.getException() != null) + throw res.getException(); + } + + return null; + } + + /** The job for enable/disable events. */ + private static class EventManagementJob extends VisorJob { + /** Serial version uid. */ + private static final long serialVersionUID = 0L; + + /** + * @param arg Job argument. + * @param debug Flag indicating whether debug information should be printed into node log. + */ + protected EventManagementJob(EventCommandArg arg, boolean debug) { + super(arg, debug); + } + + /** {@inheritDoc} */ + @Override protected Void run(EventCommandArg arg) throws IgniteException { + Map evtToName = U.gridEventNames(); + + Map nameToEvt = evtToName.entrySet().stream() + .collect(Collectors.toMap(e -> "EVT_" + e.getValue(), Map.Entry::getKey)); + + int[] evtTypes = new int[arg.events.length]; + + for (int i = 0; i < arg.events.length; i++) { + String evtName = arg.events[i]; + + if (!nameToEvt.containsKey(evtName)) + throw new IgniteException("Failed to find event by name [evt=" + evtName + ']'); + + evtTypes[i] = nameToEvt.get(evtName); + } + + if (arg instanceof EventEnableCommand.EventEnableCommandArg) + ignite.context().event().enableEvents(evtTypes); + else + ignite.context().event().disableEvents(evtTypes); + + return null; + } + + /** {@inheritDoc} */ + @Override public SecurityPermissionSet requiredPermissions() { + return systemPermissions( + argument(0) instanceof EventEnableCommand.EventEnableCommandArg ? EVENTS_ENABLE : EVENTS_DISABLE + ); + } + } +} diff --git a/modules/core/src/main/java/org/apache/ignite/internal/managers/eventstorage/GridEventStorageManager.java b/modules/core/src/main/java/org/apache/ignite/internal/managers/eventstorage/GridEventStorageManager.java index 38a773de2bb4b..1f96aa0a316d9 100644 --- a/modules/core/src/main/java/org/apache/ignite/internal/managers/eventstorage/GridEventStorageManager.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/managers/eventstorage/GridEventStorageManager.java @@ -54,6 +54,7 @@ import org.apache.ignite.internal.managers.discovery.DiscoCache; import org.apache.ignite.internal.processors.platform.PlatformEventFilterListener; import org.apache.ignite.internal.util.GridConcurrentLinkedHashSet; +import org.apache.ignite.internal.util.GridIntList; import org.apache.ignite.internal.util.future.GridFutureAdapter; import org.apache.ignite.internal.util.typedef.F; import org.apache.ignite.internal.util.typedef.X; @@ -65,6 +66,7 @@ import org.apache.ignite.marshaller.Marshaller; import org.apache.ignite.plugin.security.SecurityPermission; import org.apache.ignite.spi.IgniteSpiException; +import org.apache.ignite.spi.discovery.DiscoveryDataBag; import org.apache.ignite.spi.eventstorage.EventStorageSpi; import org.apache.ignite.spi.eventstorage.NoopEventStorageSpi; import org.apache.ignite.spi.eventstorage.memory.MemoryEventStorageSpi; @@ -83,6 +85,7 @@ import static org.apache.ignite.events.EventType.EVT_NODE_METRICS_UPDATED; import static org.apache.ignite.events.EventType.EVT_TASK_FAILED; import static org.apache.ignite.events.EventType.EVT_TASK_FINISHED; +import static org.apache.ignite.internal.GridComponent.DiscoveryDataExchangeType.EVENT_MGR; import static org.apache.ignite.internal.GridTopic.TOPIC_EVENT; import static org.apache.ignite.internal.events.DiscoveryCustomEvent.EVT_DISCOVERY_CUSTOM_EVT; import static org.apache.ignite.internal.managers.communication.GridIoPolicy.PUBLIC_POOL; @@ -385,11 +388,20 @@ public int[] enabledEvents() { * * @param types Events to enable. */ - public synchronized void enableEvents(int[] types) { - assert types != null; - + public void enableEvents(int[] types) { ctx.security().authorize(SecurityPermission.EVENTS_ENABLE); + enableEvents0(types); + } + + /** + * Enables provided events (without authorization). + * + * @param types Events to enable. + */ + private synchronized void enableEvents0(int[] types) { + assert types != null; + boolean[] userRecordableEvts0 = userRecordableEvts; boolean[] recordableEvts0 = recordableEvts; int[] inclEvtTypes0 = inclEvtTypes; @@ -427,11 +439,20 @@ public synchronized void enableEvents(int[] types) { * * @param types Events to disable. */ - public synchronized void disableEvents(int[] types) { - assert types != null; - + public void disableEvents(int[] types) { ctx.security().authorize(SecurityPermission.EVENTS_DISABLE); + disableEvents0(types); + } + + /** + * Disables provided events (without authorization). + * + * @param types Events to disable. + */ + private synchronized void disableEvents0(int[] types) { + assert types != null; + boolean[] userRecordableEvts0 = userRecordableEvts; boolean[] recordableEvts0 = recordableEvts; int[] inclEvtTypes0 = inclEvtTypes; @@ -440,12 +461,6 @@ public synchronized void disableEvents(int[] types) { int userTypesLen = 0; for (int type : types) { - if (binarySearch(cfgInclEvtTypes, type)) { - U.warn(log, "Can't disable event since it was enabled in configuration: " + U.gridEventName(type)); - - continue; - } - if (type < len) { userRecordableEvts0[type] = false; @@ -1151,6 +1166,52 @@ private int[] copy(int[] arr) { return Arrays.copyOf(arr, arr.length); } + /** {@inheritDoc} */ + @Override public DiscoveryDataExchangeType discoveryDataType() { + return DiscoveryDataExchangeType.EVENT_MGR; + } + + /** {@inheritDoc} */ + @Override public void onGridDataReceived(DiscoveryDataBag.GridDiscoveryData data) { + if (data.commonData() == null) + return; + + if (ctx.clientNode()) + return; + + GridIntList clusterData = new GridIntList((int[])data.commonData()); + GridIntList nodeData = new GridIntList(enabledEvents()); + + GridIntList toEnable = new GridIntList(clusterData.size()); + GridIntList toDisable = new GridIntList(nodeData.size()); + + for (int i = 0; i < clusterData.size(); i++) { + if (!nodeData.contains(clusterData.get(i))) + toEnable.add(clusterData.get(i)); + } + + for (int i = 0; i < nodeData.size(); i++) { + if (!clusterData.contains(nodeData.get(i))) + toDisable.add(nodeData.get(i)); + } + + if (!toEnable.isEmpty()) + enableEvents0(toEnable.arrayCopy()); + + if (!toDisable.isEmpty()) + disableEvents0(toDisable.arrayCopy()); + } + + /** {@inheritDoc} */ + @Override public void collectGridNodeData(DiscoveryDataBag dataBag) { + if (dataBag.isJoiningNodeClient() && dataBag.commonDataCollectedFor(EVENT_MGR.ordinal())) + return; + + int[] clusterData = enabledEvents(); + + dataBag.addGridCommonData(EVENT_MGR.ordinal(), clusterData); + } + /** * */ diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java index 41783b4d8155b..94b0aaab023b8 100755 --- a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java @@ -705,6 +705,15 @@ public static String gridEventName(int type) { return name != null ? name : Integer.toString(type); } + /** + * Gets known grid events with names. + * + * @return Map of event types to names. + */ + public static Map gridEventNames() { + return Collections.unmodifiableMap(GRID_EVT_NAMES); + } + /** * Gets all event types. * diff --git a/modules/core/src/test/java/org/apache/ignite/internal/GridEventStorageRuntimeConfigurationSelfTest.java b/modules/core/src/test/java/org/apache/ignite/internal/GridEventStorageRuntimeConfigurationSelfTest.java index 1fad7f3ce1b72..f99a71ae8c5ac 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/GridEventStorageRuntimeConfigurationSelfTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/GridEventStorageRuntimeConfigurationSelfTest.java @@ -251,11 +251,11 @@ public void testGetters() throws Exception { g.events().disableLocal(20000, 20001, 30000, EVT_TASK_STARTED, EVT_CACHE_ENTRY_CREATED); assertEqualsWithoutOrder( - new int[] {EVT_TASK_FINISHED, EVT_TASK_STARTED, 30000}, + new int[] {EVT_TASK_FINISHED}, getEnabledEvents(g)); assertEqualsWithoutOrder( - new int[] {EVT_TASK_FINISHED, EVT_TASK_STARTED, 30000}, + new int[] {EVT_TASK_FINISHED}, getEnabledEvents(1013, g, 20000, 30000)); int[] a = new int[1013]; @@ -273,19 +273,18 @@ public void testGetters() throws Exception { g.events().enableLocal(Arrays.copyOf(a, 1002)); a0[1002] = EVT_TASK_FINISHED; - a0[1003] = 30000; - assertEqualsWithoutOrder(Arrays.copyOf(a0, 1004), getEnabledEvents(g)); - assertEqualsWithoutOrder(Arrays.copyOf(a0, 1004), getEnabledEvents(2013, g, 30000)); + assertEqualsWithoutOrder(Arrays.copyOf(a0, 1003), getEnabledEvents(g)); + assertEqualsWithoutOrder(Arrays.copyOf(a0, 1003), getEnabledEvents(2013, g, 30000)); g.events().disableLocal(Arrays.copyOf(a, 1002)); assertEqualsWithoutOrder( - new int[] {EVT_TASK_STARTED, EVT_TASK_FINISHED, 30000}, + new int[] {EVT_TASK_FINISHED}, getEnabledEvents(g)); assertEqualsWithoutOrder( - new int[] {EVT_TASK_STARTED, EVT_TASK_FINISHED, 30000}, + new int[] {EVT_TASK_FINISHED}, getEnabledEvents(1013, g, 20000, 30000)); } finally { diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/rebalancing/GridCacheRebalanceOrderTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/rebalancing/GridCacheRebalanceOrderTest.java index 99ecc1921a670..0a49298f5f3ac 100644 --- a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/rebalancing/GridCacheRebalanceOrderTest.java +++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/distributed/rebalancing/GridCacheRebalanceOrderTest.java @@ -70,10 +70,10 @@ public class GridCacheRebalanceOrderTest extends GridCommonAbstractTest { }, new int[] {EVT_CACHE_REBALANCE_STOPPED}); cfg.setLocalEventListeners(listeners); - - cfg.setIncludeEventTypes(EventType.EVTS_ALL); } + cfg.setIncludeEventTypes(EventType.EVTS_ALL); + return cfg; } diff --git a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_help.output b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_help.output index ade6902b3a50b..6e4cfb5e299d0 100644 --- a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_help.output +++ b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassTest_help.output @@ -504,6 +504,24 @@ If the file name isn't specified the output file name is: '.bin': Finalize partitions update counters: control.(sh|bat) --consistency finalize + Enable events on all server nodes: + control.(sh|bat) --event enable event1[,...,eventN] + + Parameters: + event1[,...,eventN] - Comma separated list of events. + + Disable events on all server nodes: + control.(sh|bat) --event disable event1[,...,eventN] + + Parameters: + event1[,...,eventN] - Comma separated list of events. + + List and status of events: + control.(sh|bat) --event list [--enabled] + + Parameters: + --enabled - Filter only enabled events. + By default commands affecting the cluster require interactive confirmation. Use --yes option to disable it. diff --git a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_help.output b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_help.output index 834742e78ebb7..b1d62c5834a8c 100644 --- a/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_help.output +++ b/modules/core/src/test/resources/org.apache.ignite.util/GridCommandHandlerClusterByClassWithSSLTest_help.output @@ -504,6 +504,24 @@ If the file name isn't specified the output file name is: '.bin': Finalize partitions update counters: control.(sh|bat) --consistency finalize + Enable events on all server nodes: + control.(sh|bat) --event enable event1[,...,eventN] + + Parameters: + event1[,...,eventN] - Comma separated list of events. + + Disable events on all server nodes: + control.(sh|bat) --event disable event1[,...,eventN] + + Parameters: + event1[,...,eventN] - Comma separated list of events. + + List and status of events: + control.(sh|bat) --event list [--enabled] + + Parameters: + --enabled - Filter only enabled events. + By default commands affecting the cluster require interactive confirmation. Use --yes option to disable it.