From 723d215fdf7ebb98d3fa3cbd05e524b9b413c955 Mon Sep 17 00:00:00 2001 From: Tomas Olvecky Date: Wed, 18 Dec 2013 18:15:58 +0100 Subject: [PATCH] Add shutdown hook. Implement shutdown bundle that stops system bundle on JMX, netconf RPC, or when invoked on shutdown service taken from OSGi service registry. User must provide a shutdown secret set during initial module configuration and may provide a reason of shutdown. Currently this bundle is not instanciated during server startup. Change-Id: I4ca652265fc676c9f387c7caa49bca499abd4400 Signed-off-by: Tomas Olvecky --- opendaylight/config/config-api/pom.xml | 4 - .../config/api/ValidationException.java | 4 +- .../manager/impl/AbstractConfigTest.java | 31 ++++ .../config/config-plugin-parent/pom.xml | 1 + opendaylight/config/pom.xml | 37 ++++- opendaylight/config/shutdown-api/pom.xml | 47 ++++++ .../config/shutdown/ShutdownService.java | 21 +++ .../shutdown-api/src/main/yang/shutdown.yang | 29 ++++ opendaylight/config/shutdown-impl/pom.xml | 64 ++++++++ .../yang/shutdown/impl/ShutdownModule.java | 91 +++++++++++ .../shutdown/impl/ShutdownModuleFactory.java | 49 ++++++ .../shutdown/impl/ShutdownServiceImpl.java | 104 ++++++++++++ .../src/main/yang/shutdown-impl.yang | 71 +++++++++ .../yang/shutdown/impl/ShutdownTest.java | 149 ++++++++++++++++++ .../yangjmxgenerator/RuntimeBeanEntry.java | 65 ++++---- .../databaseinteractions/chunked1.txt | 35 ---- .../databaseinteractions/chunked2.txt | 48 ------ .../databaseinteractions/chunked3.txt | 43 ----- .../databaseinteractions/chunked4.txt | 40 ----- .../databaseinteractions/chunked5.txt | 42 ----- .../jolokia_config_bean_response.txt | 18 --- .../jolokia_lookupConfigBeans.txt | 18 --- .../notused/client_commit.xml | 4 - .../notused/client_lock_candidate.xml | 8 - .../notused/client_lock_running.xml | 8 - .../notused/client_modify_candidate.xml | 20 --- .../notused/client_unlock_candidate.xml | 8 - .../notused/client_unlock_running.xml | 8 - .../server_error_missing_attribute.xml | 11 -- .../distribution/opendaylight/pom.xml | 11 ++ 30 files changed, 735 insertions(+), 354 deletions(-) create mode 100644 opendaylight/config/shutdown-api/pom.xml create mode 100644 opendaylight/config/shutdown-api/src/main/java/org/opendaylight/controller/config/shutdown/ShutdownService.java create mode 100644 opendaylight/config/shutdown-api/src/main/yang/shutdown.yang create mode 100644 opendaylight/config/shutdown-impl/pom.xml create mode 100644 opendaylight/config/shutdown-impl/src/main/java/org/opendaylight/controller/config/yang/shutdown/impl/ShutdownModule.java create mode 100644 opendaylight/config/shutdown-impl/src/main/java/org/opendaylight/controller/config/yang/shutdown/impl/ShutdownModuleFactory.java create mode 100644 opendaylight/config/shutdown-impl/src/main/java/org/opendaylight/controller/config/yang/shutdown/impl/ShutdownServiceImpl.java create mode 100644 opendaylight/config/shutdown-impl/src/main/yang/shutdown-impl.yang create mode 100644 opendaylight/config/shutdown-impl/src/test/java/org/opendaylight/controller/config/yang/shutdown/impl/ShutdownTest.java delete mode 100644 opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked1.txt delete mode 100644 opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked2.txt delete mode 100644 opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked3.txt delete mode 100644 opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked4.txt delete mode 100644 opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked5.txt delete mode 100644 opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/databaseinteractions/jolokia_config_bean_response.txt delete mode 100644 opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/databaseinteractions/jolokia_lookupConfigBeans.txt delete mode 100644 opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_commit.xml delete mode 100644 opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_lock_candidate.xml delete mode 100644 opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_lock_running.xml delete mode 100644 opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_modify_candidate.xml delete mode 100644 opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_unlock_candidate.xml delete mode 100644 opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_unlock_running.xml delete mode 100644 opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/server_error_missing_attribute.xml diff --git a/opendaylight/config/config-api/pom.xml b/opendaylight/config/config-api/pom.xml index 38fb8ed66b..10c9fad07e 100644 --- a/opendaylight/config/config-api/pom.xml +++ b/opendaylight/config/config-api/pom.xml @@ -58,10 +58,6 @@ org.opendaylight.yangtools yang-maven-plugin - - org.codehaus.mojo - build-helper-maven-plugin - diff --git a/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/ValidationException.java b/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/ValidationException.java index 90b8bb6d85..da54a8341a 100644 --- a/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/ValidationException.java +++ b/opendaylight/config/config-api/src/main/java/org/opendaylight/controller/config/api/ValidationException.java @@ -21,7 +21,7 @@ import java.util.Map.Entry; public class ValidationException extends RuntimeException { private static final long serialVersionUID = -6072893219820274247L; - private final Map> failedValidations; + private final Map> failedValidations; public ValidationException( Map> failedValidations) { @@ -70,7 +70,7 @@ public class ValidationException extends RuntimeException { return new ValidationException(failedValidations); } - public Map> getFailedValidations() { + public Map> getFailedValidations() { return failedValidations; } diff --git a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/AbstractConfigTest.java b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/AbstractConfigTest.java index 028d7d1f40..64ce14e8f1 100644 --- a/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/AbstractConfigTest.java +++ b/opendaylight/config/config-manager/src/test/java/org/opendaylight/controller/config/manager/impl/AbstractConfigTest.java @@ -31,12 +31,17 @@ import org.slf4j.LoggerFactory; import javax.management.InstanceAlreadyExistsException; import javax.management.MBeanServer; import javax.management.ObjectName; +import javax.management.RuntimeMBeanException; import java.io.Closeable; import java.io.InputStream; import java.lang.management.ManagementFactory; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; import java.util.Dictionary; import java.util.List; import java.util.Set; @@ -249,4 +254,30 @@ public abstract class AbstractConfigTest extends } } } + + /** + * Expand inner exception wrapped by JMX + * + * @param innerObject jmx proxy which will be wrapped and returned + */ + protected T rethrowCause(final T innerObject) { + + Object proxy = Proxy.newProxyInstance(innerObject.getClass().getClassLoader(), + innerObject.getClass().getInterfaces(), new InvocationHandler() { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + try { + return method.invoke(innerObject, args); + } catch (InvocationTargetException e) { + try { + throw e.getTargetException(); + } catch (RuntimeMBeanException e2) { + throw e2.getTargetException(); + } + } + } + }); + return (T) proxy; + } + } diff --git a/opendaylight/config/config-plugin-parent/pom.xml b/opendaylight/config/config-plugin-parent/pom.xml index 7696ae5573..0117098ee1 100644 --- a/opendaylight/config/config-plugin-parent/pom.xml +++ b/opendaylight/config/config-plugin-parent/pom.xml @@ -72,6 +72,7 @@ ${jmxGeneratorPath} + ${salGeneratorPath} diff --git a/opendaylight/config/pom.xml b/opendaylight/config/pom.xml index 9f9bca9d98..ca576824fa 100644 --- a/opendaylight/config/pom.xml +++ b/opendaylight/config/pom.xml @@ -42,6 +42,8 @@ config-persister-directory-adapter config-persister-directory-xml-adapter yang-test-plugin + shutdown-api + shutdown-impl @@ -68,6 +70,7 @@ 0.5.9-SNAPSHOT 0.6.0-SNAPSHOT 0.1.1-SNAPSHOT + ${project.build.directory}/generated-sources/sal @@ -212,6 +215,12 @@ ${config.version} + + org.opendaylight.controller + shutdown-api + ${config.version} + + org.opendaylight.yangtools @@ -269,10 +278,8 @@ org.apache.maven.plugins maven-surefire-plugin - - org.codehaus.mojo - build-helper-maven-plugin - + + @@ -295,7 +302,7 @@ org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl - target/generated-sources/sal + ${salGeneratorPath} @@ -316,6 +323,26 @@ + + + org.codehaus.mojo + build-helper-maven-plugin + 1.8 + + + add-source + generate-sources + + add-source + + + + ${salGeneratorPath} + + + + + org.apache.maven.plugins maven-jar-plugin diff --git a/opendaylight/config/shutdown-api/pom.xml b/opendaylight/config/shutdown-api/pom.xml new file mode 100644 index 0000000000..0603b2d82d --- /dev/null +++ b/opendaylight/config/shutdown-api/pom.xml @@ -0,0 +1,47 @@ + + + + config-plugin-parent + org.opendaylight.controller + 0.2.3-SNAPSHOT + ../config-plugin-parent + + 4.0.0 + bundle + shutdown-api + + + + com.google.guava + guava + + + org.opendaylight.controller + config-api + + + + + + + org.apache.felix + maven-bundle-plugin + + + + org.opendaylight.controller.config.shutdown, + org.opendaylight.controller.config.yang.shutdown + + + + + + org.opendaylight.yangtools + yang-maven-plugin + + + + + diff --git a/opendaylight/config/shutdown-api/src/main/java/org/opendaylight/controller/config/shutdown/ShutdownService.java b/opendaylight/config/shutdown-api/src/main/java/org/opendaylight/controller/config/shutdown/ShutdownService.java new file mode 100644 index 0000000000..104241757f --- /dev/null +++ b/opendaylight/config/shutdown-api/src/main/java/org/opendaylight/controller/config/shutdown/ShutdownService.java @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.config.shutdown; + +import com.google.common.base.Optional; + +public interface ShutdownService { + + /** + * Shut down the server. + * + * @param inputSecret must match configured secret of the implementation + * @param reason Optional string to be logged while shutting down + */ + void shutdown(String inputSecret, Optional reason); +} diff --git a/opendaylight/config/shutdown-api/src/main/yang/shutdown.yang b/opendaylight/config/shutdown-api/src/main/yang/shutdown.yang new file mode 100644 index 0000000000..eb09178841 --- /dev/null +++ b/opendaylight/config/shutdown-api/src/main/yang/shutdown.yang @@ -0,0 +1,29 @@ +module shutdown { + yang-version 1; + namespace "urn:opendaylight:params:xml:ns:yang:controller:shutdown"; + prefix "shutdown"; + + import config { prefix config; revision-date 2013-04-05; } + + description + "This module contains the base YANG definitions for + shutdown service. + + Copyright (c)2013 Cisco Systems, Inc. All rights reserved.; + + This program and the accompanying materials are made available + under the terms of the Eclipse Public License v1.0 which + accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html"; + + revision "2013-12-18" { + description + "Initial revision."; + } + + identity shutdown { + base "config:service-type"; + config:java-class "org.opendaylight.controller.config.shutdown.ShutdownService"; + } + +} diff --git a/opendaylight/config/shutdown-impl/pom.xml b/opendaylight/config/shutdown-impl/pom.xml new file mode 100644 index 0000000000..4fcb0c89f0 --- /dev/null +++ b/opendaylight/config/shutdown-impl/pom.xml @@ -0,0 +1,64 @@ + + + + config-plugin-parent + org.opendaylight.controller + 0.2.3-SNAPSHOT + ../config-plugin-parent + + 4.0.0 + bundle + shutdown-impl + + + + com.google.guava + guava + + + org.opendaylight.controller + config-api + + + org.opendaylight.controller + shutdown-api + + + org.opendaylight.controller + config-manager + test + test-jar + + + org.opendaylight.controller + config-manager + test + + + org.opendaylight.controller + config-util + test + + + org.opendaylight.bgpcep + mockito-configuration + test + + + + + + + org.apache.felix + maven-bundle-plugin + + + org.opendaylight.yangtools + yang-maven-plugin + + + + + diff --git a/opendaylight/config/shutdown-impl/src/main/java/org/opendaylight/controller/config/yang/shutdown/impl/ShutdownModule.java b/opendaylight/config/shutdown-impl/src/main/java/org/opendaylight/controller/config/yang/shutdown/impl/ShutdownModule.java new file mode 100644 index 0000000000..f6937f9ef1 --- /dev/null +++ b/opendaylight/config/shutdown-impl/src/main/java/org/opendaylight/controller/config/yang/shutdown/impl/ShutdownModule.java @@ -0,0 +1,91 @@ +/** + * Generated file + + * Generated from: yang module name: shutdown-impl yang module local name: shutdown + * Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator + * Generated at: Wed Dec 18 14:02:06 CET 2013 + * + * Do not modify this file unless it is present under src/main directory + */ +package org.opendaylight.controller.config.yang.shutdown.impl; + +import org.opendaylight.controller.config.api.DependencyResolver; +import org.opendaylight.controller.config.api.JmxAttributeValidationException; +import org.opendaylight.controller.config.api.ModuleIdentifier; +import org.osgi.framework.Bundle; + +public final class ShutdownModule extends AbstractShutdownModule { + private final Bundle systemBundle; + private final ShutdownModule nullableOldModule; + + public ShutdownModule(ModuleIdentifier identifier, Bundle systemBundle) { + super(identifier, null); + singletonCheck(identifier); + this.systemBundle = systemBundle; + this.nullableOldModule = null; + } + + public ShutdownModule(ModuleIdentifier identifier, ShutdownModule oldModule, java.lang.AutoCloseable oldInstance, + Bundle systemBundle) { + super(identifier, null, oldModule, oldInstance); + singletonCheck(identifier); + this.systemBundle = systemBundle; + this.nullableOldModule = oldModule; + } + + private static void singletonCheck(ModuleIdentifier identifier) { + if (AbstractShutdownModuleFactory.NAME.equals(identifier.getInstanceName()) == false) { + throw new IllegalArgumentException("Singleton enforcement failed. Expected instance name " + AbstractShutdownModuleFactory.NAME); + } + } + + @Deprecated // needed for generated code + public ShutdownModule(ModuleIdentifier identifier, DependencyResolver dependencyResolver, ShutdownModule oldModule, + AutoCloseable oldInstance) { + super(identifier, dependencyResolver, oldModule, oldInstance); + throw new UnsupportedOperationException(); + } + + @Deprecated // needed for generated code + public ShutdownModule(ModuleIdentifier identifier, DependencyResolver dependencyResolver) { + super(identifier, dependencyResolver); + throw new UnsupportedOperationException(); + } + + @Override + public String getSecret() { + throw new UnsupportedOperationException(); + } + + @Override + public String getOldSecret() { + throw new UnsupportedOperationException(); + } + + String getActualSecret() { + return super.getSecret(); + } + + String getActualOldSecret() { + return super.getOldSecret(); + } + + @Override + protected void customValidation() { + JmxAttributeValidationException.checkNotNull(super.getOldSecret(), oldSecretJmxAttribute); + JmxAttributeValidationException.checkNotNull(super.getSecret(), secretJmxAttribute); + if (nullableOldModule != null) { + // if nothing changed, remain valid + boolean sameAsOldModule = isSame(nullableOldModule); + if (sameAsOldModule == false) { + boolean valid = getActualOldSecret().equals(nullableOldModule.getActualSecret()); + JmxAttributeValidationException.checkCondition(valid, "Invalid old secret", oldSecretJmxAttribute); + } + } + } + + @Override + public java.lang.AutoCloseable createInstance() { + return new ShutdownServiceImpl(getActualSecret(), systemBundle, getRootRuntimeBeanRegistratorWrapper()); + } +} diff --git a/opendaylight/config/shutdown-impl/src/main/java/org/opendaylight/controller/config/yang/shutdown/impl/ShutdownModuleFactory.java b/opendaylight/config/shutdown-impl/src/main/java/org/opendaylight/controller/config/yang/shutdown/impl/ShutdownModuleFactory.java new file mode 100644 index 0000000000..637395bc48 --- /dev/null +++ b/opendaylight/config/shutdown-impl/src/main/java/org/opendaylight/controller/config/yang/shutdown/impl/ShutdownModuleFactory.java @@ -0,0 +1,49 @@ +/** + * Generated file + + * Generated from: yang module name: shutdown-impl yang module local name: shutdown + * Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator + * Generated at: Wed Dec 18 14:02:06 CET 2013 + * + * Do not modify this file unless it is present under src/main directory + */ +package org.opendaylight.controller.config.yang.shutdown.impl; + +import org.opendaylight.controller.config.api.DependencyResolver; +import org.opendaylight.controller.config.api.ModuleIdentifier; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; + +public class ShutdownModuleFactory extends AbstractShutdownModuleFactory { + + @Override + public org.opendaylight.controller.config.spi.Module createModule(String instanceName, org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, org.opendaylight.controller.config.api.DynamicMBeanWithInstance old, org.osgi.framework.BundleContext bundleContext) throws Exception { + org.opendaylight.controller.config.yang.shutdown.impl.ShutdownModule oldModule = null; + try { + oldModule = (org.opendaylight.controller.config.yang.shutdown.impl.ShutdownModule) old.getModule(); + } catch(Exception e) { + return handleChangedClass(old); + } + org.opendaylight.controller.config.yang.shutdown.impl.ShutdownModule module = instantiateModule(instanceName, dependencyResolver, oldModule, old.getInstance(), bundleContext); + + module.setOldSecret(oldModule.getActualOldSecret()); + module.setSecret(oldModule.getActualSecret()); + + return module; + } + + + public ShutdownModule instantiateModule(String instanceName, DependencyResolver dependencyResolver, + ShutdownModule oldModule, AutoCloseable oldInstance, + BundleContext bundleContext) { + Bundle systemBundle = bundleContext.getBundle(0); + return new ShutdownModule(new ModuleIdentifier(NAME, instanceName), oldModule, oldInstance, systemBundle); + } + + + public ShutdownModule instantiateModule(String instanceName, DependencyResolver dependencyResolver, + BundleContext bundleContext) { + Bundle systemBundle = bundleContext.getBundle(0); + return new ShutdownModule(new ModuleIdentifier(NAME, instanceName), systemBundle); + } +} diff --git a/opendaylight/config/shutdown-impl/src/main/java/org/opendaylight/controller/config/yang/shutdown/impl/ShutdownServiceImpl.java b/opendaylight/config/shutdown-impl/src/main/java/org/opendaylight/controller/config/yang/shutdown/impl/ShutdownServiceImpl.java new file mode 100644 index 0000000000..e5b95c812b --- /dev/null +++ b/opendaylight/config/shutdown-impl/src/main/java/org/opendaylight/controller/config/yang/shutdown/impl/ShutdownServiceImpl.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.config.yang.shutdown.impl; + +import com.google.common.base.Optional; +import org.opendaylight.controller.config.shutdown.ShutdownService; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ShutdownServiceImpl implements ShutdownService, AutoCloseable { + private final ShutdownService impl; + private final ShutdownRuntimeRegistration registration; + + public ShutdownServiceImpl(String secret, Bundle systemBundle, + ShutdownRuntimeRegistrator rootRuntimeBeanRegistratorWrapper) { + if (secret == null) { + throw new IllegalArgumentException("Secret cannot be null"); + } + impl = new Impl(secret, systemBundle); + registration = rootRuntimeBeanRegistratorWrapper.register(new MXBeanImpl(impl)); + } + + @Override + public void shutdown(String inputSecret, Optional reason) { + impl.shutdown(inputSecret, reason); + } + + @Override + public void close() { + registration.close(); + } +} + +class Impl implements ShutdownService { + private static final Logger logger = LoggerFactory.getLogger(Impl.class); + private final String secret; + private final Bundle systemBundle; + + Impl(String secret, Bundle systemBundle) { + this.secret = secret; + this.systemBundle = systemBundle; + } + + @Override + public void shutdown(String inputSecret, Optional reason) { + logger.warn("Shutdown issued with secret {} and reason {}", inputSecret, reason); + try { + Thread.sleep(1000); // prevent brute force attack + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + logger.warn("Shutdown process interrupted", e); + } + if (this.secret.equals(inputSecret)) { + logger.info("Server is shutting down"); + + Thread stopSystemBundle = new Thread() { + @Override + public void run() { + try { + // wait so that JMX response is received + Thread.sleep(1000); + systemBundle.stop(); + } catch (BundleException e) { + logger.warn("Can not stop OSGi server", e); + } catch (InterruptedException e) { + logger.warn("Shutdown process interrupted", e); + } + } + }; + stopSystemBundle.start(); + + } else { + logger.warn("Unauthorized attempt to shut down server"); + throw new IllegalArgumentException("Invalid secret"); + } + } + +} + +class MXBeanImpl implements ShutdownRuntimeMXBean { + private final ShutdownService impl; + + MXBeanImpl(ShutdownService impl) { + this.impl = impl; + } + + @Override + public void shutdown(String inputSecret, String nullableReason) { + Optional optionalReason; + if (nullableReason == null) { + optionalReason = Optional.absent(); + } else { + optionalReason = Optional.of(nullableReason); + } + impl.shutdown(inputSecret, optionalReason); + } +} diff --git a/opendaylight/config/shutdown-impl/src/main/yang/shutdown-impl.yang b/opendaylight/config/shutdown-impl/src/main/yang/shutdown-impl.yang new file mode 100644 index 0000000000..78b44abfb2 --- /dev/null +++ b/opendaylight/config/shutdown-impl/src/main/yang/shutdown-impl.yang @@ -0,0 +1,71 @@ +module shutdown-impl { + yang-version 1; + namespace "urn:opendaylight:params:xml:ns:yang:controller:shutdown:impl"; + prefix "shutdown-impl"; + + import shutdown { prefix shutdown; revision-date 2013-12-18; } + import config { prefix config; revision-date 2013-04-05; } + import rpc-context { prefix rpcx; revision-date 2013-06-17; } + + organization "Cisco Systems, Inc."; + + description + "This module contains the base YANG definitions for + shutdown implementation. + + Copyright (c)2013 Cisco Systems, Inc. All rights reserved.; + + This program and the accompanying materials are made available + under the terms of the Eclipse Public License v1.0 which + accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html"; + + revision "2013-12-18" { + description + "Initial revision."; + } + + identity shutdown { + base config:module-type; + config:provided-service shutdown:shutdown; + } + + augment "/config:modules/config:module/config:configuration" { + case shutdown { + when "/config:modules/config:module/config:type = 'shutdown'"; + leaf secret { + type string; + default ""; + } + leaf old-secret { + type string; + default ""; + } + } + } + + augment "/config:modules/config:module/config:state" { + case shutdown { + when "/config:modules/config:module/config:type = 'shutdown'"; + rpcx:rpc-context-instance "shutdown-rpc"; + } + } + + identity shutdown-rpc; + + rpc shutdown { + input { + uses rpcx:rpc-context-ref { + refine context-instance { + rpcx:rpc-context-instance shutdown-rpc; + } + } + leaf input-secret { + type string; + } + leaf reason { + type string; + } + } + } +} diff --git a/opendaylight/config/shutdown-impl/src/test/java/org/opendaylight/controller/config/yang/shutdown/impl/ShutdownTest.java b/opendaylight/config/shutdown-impl/src/test/java/org/opendaylight/controller/config/yang/shutdown/impl/ShutdownTest.java new file mode 100644 index 0000000000..86cd6fa812 --- /dev/null +++ b/opendaylight/config/shutdown-impl/src/test/java/org/opendaylight/controller/config/yang/shutdown/impl/ShutdownTest.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.config.yang.shutdown.impl; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.opendaylight.controller.config.api.ValidationException; +import org.opendaylight.controller.config.api.ValidationException.ExceptionMessageWithStackTrace; +import org.opendaylight.controller.config.api.jmx.ObjectNameUtil; +import org.opendaylight.controller.config.manager.impl.AbstractConfigTest; +import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver; +import org.opendaylight.controller.config.manager.impl.factoriesresolver.ModuleFactoriesResolver; +import org.opendaylight.controller.config.util.ConfigTransactionJMXClient; +import org.osgi.framework.Bundle; + +import javax.management.JMX; +import javax.management.ObjectName; +import java.util.Collections; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.opendaylight.controller.config.yang.shutdown.impl.ShutdownModuleFactory.NAME; + +public class ShutdownTest extends AbstractConfigTest { + private final ShutdownModuleFactory factory = new ShutdownModuleFactory(); + @Mock + private Bundle mockedSysBundle; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + ModuleFactoriesResolver factoriesResolver = new HardcodedModuleFactoriesResolver(mockedContext, factory); + super.initConfigTransactionManagerImpl(factoriesResolver); + doReturn(mockedSysBundle).when(mockedContext).getBundle(0); + mockedContext.getBundle(0); + doNothing().when(mockedSysBundle).stop(); + } + + @Test + public void testSingleton_invalidName() throws Exception { + ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction(); + try { + transaction.createModule(NAME, "foo"); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("Singleton enforcement failed. Expected instance name shutdown", e.getMessage()); + } + } + + @Test + public void testWithoutSecret() throws Exception { + ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction(); + transaction.createModule(NAME, NAME); + transaction.commit(); + // test JMX rpc + ObjectName runtimeON = ObjectNameUtil.createRuntimeBeanName(NAME, NAME, Collections.emptyMap()); + ShutdownRuntimeMXBean runtime = configRegistryClient.newMXBeanProxy(runtimeON, ShutdownRuntimeMXBean.class); + try { + runtime.shutdown("foo", null); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("Invalid secret", e.getMessage()); + } + runtime.shutdown("", null); + assertStopped(); + } + + + @Test + public void testWithSecret() throws Exception { + ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction(); + ObjectName on = transaction.createModule(NAME, NAME); + ShutdownModuleMXBean proxy = transaction.newMXBeanProxy(on, ShutdownModuleMXBean.class); + String secret = "secret"; + proxy.setSecret(secret); + transaction.commit(); + shutdownViaRuntimeJMX(secret); + + // test old secret + transaction = configRegistryClient.createTransaction(); + on = transaction.lookupConfigBean(NAME, NAME); + proxy = transaction.newMXBeanProxy(on, ShutdownModuleMXBean.class); + try { + rethrowCause(proxy).getOldSecret(); + fail(); + } catch (UnsupportedOperationException e) { + } + try { + rethrowCause(proxy).getSecret(); + fail(); + } catch (UnsupportedOperationException e) { + } + // set secret to nothing + String newSecret = "newSecret"; + proxy.setSecret(newSecret); + try { + transaction.commit(); + fail("Old secret not provided - should fail validation"); + } catch (ValidationException e) { + Map> failedValidations = e.getFailedValidations(); + assertTrue(failedValidations.containsKey(NAME)); + ExceptionMessageWithStackTrace exceptionMessageWithStackTrace = failedValidations.get(NAME).get(NAME); + assertNotNull(exceptionMessageWithStackTrace); + assertEquals("OldSecret Invalid old secret", exceptionMessageWithStackTrace.getMessage()); + + } + proxy.setOldSecret(secret); + transaction.commit(); + shutdownViaRuntimeJMX(newSecret); + } + + private void shutdownViaRuntimeJMX(String secret) throws Exception { + // test JMX rpc + ObjectName runtimeON = ObjectNameUtil.createRuntimeBeanName(NAME, NAME, Collections.emptyMap()); + ShutdownRuntimeMXBean runtime = JMX.newMXBeanProxy(platformMBeanServer, runtimeON, ShutdownRuntimeMXBean.class); + try { + runtime.shutdown("", null); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("Invalid secret", e.getMessage()); + } + runtime.shutdown(secret, null); + assertStopped(); + } + + + private void assertStopped() throws Exception { + Thread.sleep(2000); // happens on another thread + verify(mockedSysBundle).stop(); + verifyNoMoreInteractions(mockedSysBundle); + reset(mockedSysBundle); + doNothing().when(mockedSysBundle).stop(); + } +} diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/RuntimeBeanEntry.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/RuntimeBeanEntry.java index f19a46d0f4..4831545b39 100644 --- a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/RuntimeBeanEntry.java +++ b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/RuntimeBeanEntry.java @@ -163,41 +163,42 @@ public class RuntimeBeanEntry { for (RpcDefinition rpc : currentModule.getRpcs()) { ContainerSchemaNode input = rpc.getInput(); - for (UsesNode uses : input.getUses()) { - - if (uses.getGroupingPath().getPath().size() != 1) - continue; - - // check grouping path - QName qname = uses.getGroupingPath().getPath().get(0); - if (false == qname - .equals(ConfigConstants.RPC_CONTEXT_REF_GROUPING_QNAME)) - continue; - - for (SchemaNode refinedNode : uses.getRefines().values()) { - - for (UnknownSchemaNode unknownSchemaNode : refinedNode - .getUnknownSchemaNodes()) { - if (ConfigConstants.RPC_CONTEXT_INSTANCE_EXTENSION_QNAME - .equals(unknownSchemaNode.getNodeType())) { - String localIdentityName = unknownSchemaNode - .getNodeParameter(); - QName identityQName = new QName( - currentModule.getNamespace(), - currentModule.getRevision(), - localIdentityName); - Set rpcDefinitions = result - .get(identityQName); - if (rpcDefinitions == null) { - throw new IllegalArgumentException( - "Identity referenced by rpc not found. Identity:" - + localIdentityName + " , rpc " - + rpc); + if (input != null) { + for (UsesNode uses : input.getUses()) { + + if (uses.getGroupingPath().getPath().size() != 1) + continue; + + // check grouping path + QName qname = uses.getGroupingPath().getPath().get(0); + if (false == qname + .equals(ConfigConstants.RPC_CONTEXT_REF_GROUPING_QNAME)) + continue; + + for (SchemaNode refinedNode : uses.getRefines().values()) { + + for (UnknownSchemaNode unknownSchemaNode : refinedNode + .getUnknownSchemaNodes()) { + if (ConfigConstants.RPC_CONTEXT_INSTANCE_EXTENSION_QNAME + .equals(unknownSchemaNode.getNodeType())) { + String localIdentityName = unknownSchemaNode + .getNodeParameter(); + QName identityQName = new QName( + currentModule.getNamespace(), + currentModule.getRevision(), + localIdentityName); + Set rpcDefinitions = result + .get(identityQName); + if (rpcDefinitions == null) { + throw new IllegalArgumentException( + "Identity referenced by rpc not found. Identity:" + + localIdentityName + " , rpc " + + rpc); + } + rpcDefinitions.add(rpc); } - rpcDefinitions.add(rpc); } } - } } } diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked1.txt b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked1.txt deleted file mode 100644 index aad72393cb..0000000000 --- a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked1.txt +++ /dev/null @@ -1,35 +0,0 @@ - -#24 - - - 14 - fred - - < -#2 -/r -#3 -pc> -## diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked2.txt b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked2.txt deleted file mode 100644 index a36a85ea1f..0000000000 --- a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked2.txt +++ /dev/null @@ -1,48 +0,0 @@ - -#22 - - - - - -#18 - - - -#19 - - -#8 - - -#77 - - - - - -## diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked3.txt b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked3.txt deleted file mode 100644 index d9dc43d620..0000000000 --- a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked3.txt +++ /dev/null @@ -1,43 +0,0 @@ - -#43 - - -#26 - - - -#35 - - - -#39 - - < -#40 -top xmlns="http://example.com/schema/1.2 -#26 -/config"> - - -#36 - - f -#56 -red - - - -#28 - - - - -## diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked4.txt b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked4.txt deleted file mode 100644 index 0b8a102dff..0000000000 --- a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked4.txt +++ /dev/null @@ -1,40 +0,0 @@ - -#17 - - - - - - -#43 - - - -#16 - - -#22 - - - -## diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked5.txt b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked5.txt deleted file mode 100644 index f8f3c4d7c2..0000000000 --- a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/chunked5.txt +++ /dev/null @@ -1,42 +0,0 @@ - -#43 - - - - - < -#4 -/tar -#18 -get> - - -#41 - - - -#29 - Ethernet0/0 - 1500 - -#61 - - - - - -## diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/databaseinteractions/jolokia_config_bean_response.txt b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/databaseinteractions/jolokia_config_bean_response.txt deleted file mode 100644 index 2ae32efb65..0000000000 --- a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/databaseinteractions/jolokia_config_bean_response.txt +++ /dev/null @@ -1,18 +0,0 @@ -curl http://localhost:17777/jolokia/read/org.opendaylight.controller:instanceName=fixed1,type=ConfigBean,interfaceName=testing-threadpool | jsonpp -{ - "request": { - "mbean": "org.opendaylight.controller:instanceName=fixed1,interfaceName=testing-threadpool,type=ConfigBean", - "type": "read" - }, - "status": 200, - "timestamp": 1362416252, - "value": { - "ExportedInterfaces": [ - "testing-threadpool", - "modifiable-threadpool" - ], - "ImplementationName": "fixed", - "ThreadCount": 10, - "TriggerNewInstanceCreation": false - } -} \ No newline at end of file diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/databaseinteractions/jolokia_lookupConfigBeans.txt b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/databaseinteractions/jolokia_lookupConfigBeans.txt deleted file mode 100644 index 2ae705a54f..0000000000 --- a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/databaseinteractions/jolokia_lookupConfigBeans.txt +++ /dev/null @@ -1,18 +0,0 @@ -$ curl 'http://localhost:17777/jolokia/exec/org.opendaylight.controller:type=ConfigRegistry/lookupConfigBeans()' | jsonpp -{ - "request": { - "mbean": "org.opendaylight.controller:type=ConfigRegistry", - "operation": "lookupConfigBeans()", - "type": "exec" - }, - "status": 200, - "timestamp": 1362417043, - "value": [ - { - "objectName": "org.opendaylight.controller:instanceName=fixed1,interfaceName=modifiable-threadpool,type=ConfigBean" - }, - { - "objectName": "org.opendaylight.controller:instanceName=fixed1,interfaceName=testing-threadpool,type=ConfigBean" - } - ] -} diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_commit.xml b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_commit.xml deleted file mode 100644 index 6eca609b6c..0000000000 --- a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_commit.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - \ No newline at end of file diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_lock_candidate.xml b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_lock_candidate.xml deleted file mode 100644 index 6a9ed639d8..0000000000 --- a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_lock_candidate.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_lock_running.xml b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_lock_running.xml deleted file mode 100644 index 2d66c45906..0000000000 --- a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_lock_running.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_modify_candidate.xml b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_modify_candidate.xml deleted file mode 100644 index ce67845de1..0000000000 --- a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_modify_candidate.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - none - test-then-set - stop-on-error - - - - 7 - - - - - \ No newline at end of file diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_unlock_candidate.xml b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_unlock_candidate.xml deleted file mode 100644 index dd6fe1ba1e..0000000000 --- a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_unlock_candidate.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_unlock_running.xml b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_unlock_running.xml deleted file mode 100644 index f94af4698d..0000000000 --- a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/client_unlock_running.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/server_error_missing_attribute.xml b/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/server_error_missing_attribute.xml deleted file mode 100644 index c70184e2b4..0000000000 --- a/opendaylight/controller/netconf/netconf-impl/src/test/resources/org/opendaylight/netconf/impl/listener/databaseinteractions/notused/server_error_missing_attribute.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - rpc - missing-attribute - error - - message-id - rpc - - - diff --git a/opendaylight/distribution/opendaylight/pom.xml b/opendaylight/distribution/opendaylight/pom.xml index 6fca9db636..7a4bc4a031 100644 --- a/opendaylight/distribution/opendaylight/pom.xml +++ b/opendaylight/distribution/opendaylight/pom.xml @@ -264,6 +264,17 @@ ${config.version} + + org.opendaylight.controller + shutdown-api + ${config.version} + + + org.opendaylight.controller + shutdown-impl + ${config.version} + + org.opendaylight.controller -- 2.36.6