.DS_STORE
.metadata
opendaylight/md-sal/sal-distributed-datastore/journal
+!opendaylight/distribution/opendaylight-karaf-resources/src/main/resources/bin
+
</parent>
<artifactId>features-config-netty</artifactId>
- <packaging>pom</packaging>
+ <packaging>jar</packaging>
<properties>
<features.file>features.xml</features.file>
<artifactId>features-config-persister</artifactId>
<classifier>features</classifier>
<type>xml</type>
- <scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<groupId>org.opendaylight.controller</groupId>
<artifactId>config-netty-config</artifactId>
</dependency>
+ <!-- test to validate features.xml -->
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>features-test</artifactId>
+ </dependency>
</dependencies>
<build>
</execution>
</executions>
</plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <systemPropertyVariables>
+ <karaf.distro.groupId>org.opendaylight.controller</karaf.distro.groupId>
+ <karaf.distro.artifactId>opendaylight-karaf-empty</karaf.distro.artifactId>
+ <karaf.distro.version>${commons.opendaylight.version}</karaf.distro.version>
+ </systemPropertyVariables>
+ <dependenciesToScan>
+ <dependency>org.opendaylight.yangtools:features-test</dependency>
+ </dependenciesToScan>
+ </configuration>
+ </plugin>
</plugins>
</build>
<scm>
</parent>
<artifactId>features-config-persister</artifactId>
- <packaging>pom</packaging>
+ <packaging>jar</packaging>
<properties>
<features.file>features.xml</features.file>
<version>${yangtools.version}</version>
<classifier>features</classifier>
<type>xml</type>
- <scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>features-netconf</artifactId>
<classifier>features</classifier>
<type>xml</type>
- <scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>features-config</artifactId>
<classifier>features</classifier>
<type>xml</type>
- <scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.moxy</artifactId>
</dependency>
+ <!-- test to validate features.xml -->
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>features-test</artifactId>
+ </dependency>
</dependencies>
<build>
</execution>
</executions>
</plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <systemPropertyVariables>
+ <karaf.distro.groupId>org.opendaylight.controller</karaf.distro.groupId>
+ <karaf.distro.artifactId>opendaylight-karaf-empty</karaf.distro.artifactId>
+ <karaf.distro.version>${commons.opendaylight.version}</karaf.distro.version>
+ </systemPropertyVariables>
+ <dependenciesToScan>
+ <dependency>org.opendaylight.yangtools:features-test</dependency>
+ </dependenciesToScan>
+ </configuration>
+ </plugin>
</plugins>
</build>
<scm>
</parent>
<artifactId>features-config</artifactId>
- <packaging>pom</packaging>
+ <packaging>jar</packaging>
<properties>
<features.file>features.xml</features.file>
<version>${yangtools.version}</version>
<classifier>features</classifier>
<type>xml</type>
- <scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<groupId>org.opendaylight.controller</groupId>
<artifactId>config-manager</artifactId>
</dependency>
+ <!-- test the features.xml -->
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>features-test</artifactId>
+ </dependency>
</dependencies>
<build>
</execution>
</executions>
</plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <systemPropertyVariables>
+ <karaf.distro.groupId>org.opendaylight.controller</karaf.distro.groupId>
+ <karaf.distro.artifactId>opendaylight-karaf-empty</karaf.distro.artifactId>
+ <karaf.distro.version>${commons.opendaylight.version}</karaf.distro.version>
+ </systemPropertyVariables>
+ <dependenciesToScan>
+ <dependency>org.opendaylight.yangtools:features-test</dependency>
+ </dependenciesToScan>
+ </configuration>
+ </plugin>
</plugins>
</build>
<scm>
<repository>mvn:org.opendaylight.yangtools/features-yangtools/${yangtools.version}/xml/features</repository>
<feature name='odl-config-all' version='${project.version}'>
- <feature version='${project.version}'>odl-mdsal-common</feature>
+ <feature version='${mdsal.version}'>odl-mdsal-common</feature>
<feature version='${project.version}'>odl-config-api</feature>
<feature version='${project.version}'>odl-config-netty-config-api</feature>
<feature version='${project.version}'>odl-config-core</feature>
</parent>
<artifactId>features-flow</artifactId>
- <packaging>pom</packaging>
+ <packaging>jar</packaging>
<properties>
<features.file>features.xml</features.file>
<version>${mdsal.version}</version>
<classifier>features</classifier>
<type>xml</type>
- <scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.opendaylight.controller.model</groupId>
<groupId>org.opendaylight.controller.md</groupId>
<artifactId>forwardingrules-manager</artifactId>
</dependency>
+ <!-- test to validate features.xml -->
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>features-test</artifactId>
+ </dependency>
</dependencies>
<build>
</execution>
</executions>
</plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <systemPropertyVariables>
+ <karaf.distro.groupId>org.opendaylight.controller</karaf.distro.groupId>
+ <karaf.distro.artifactId>opendaylight-karaf-empty</karaf.distro.artifactId>
+ <karaf.distro.version>${commons.opendaylight.version}</karaf.distro.version>
+ </systemPropertyVariables>
+ <dependenciesToScan>
+ <dependency>org.opendaylight.yangtools:features-test</dependency>
+ </dependenciesToScan>
+ </configuration>
+ </plugin>
</plugins>
</build>
<scm>
</parent>
<artifactId>features-mdsal</artifactId>
- <packaging>pom</packaging>
+ <packaging>jar</packaging>
<properties>
<features.file>features.xml</features.file>
<artifactId>features-yangtools</artifactId>
<classifier>features</classifier>
<type>xml</type>
- <scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>features-config</artifactId>
<classifier>features</classifier>
<type>xml</type>
- <scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>features-config-persister</artifactId>
<classifier>features</classifier>
<type>xml</type>
- <scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>features-config-netty</artifactId>
<classifier>features</classifier>
<type>xml</type>
- <scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<groupId>org.opendaylight.controller.samples</groupId>
<artifactId>toaster-config</artifactId>
</dependency>
+ <!-- test to validate features.xml -->
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>features-test</artifactId>
+ <version>0.6.2-SNAPSHOT</version>
+ </dependency>
</dependencies>
<build>
</execution>
</executions>
</plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <systemPropertyVariables>
+ <karaf.distro.groupId>org.opendaylight.controller</karaf.distro.groupId>
+ <karaf.distro.artifactId>opendaylight-karaf-empty</karaf.distro.artifactId>
+ <karaf.distro.version>${commons.opendaylight.version}</karaf.distro.version>
+ </systemPropertyVariables>
+ <dependenciesToScan>
+ <dependency>org.opendaylight.yangtools:features-test</dependency>
+ </dependenciesToScan>
+ </configuration>
+ </plugin>
</plugins>
</build>
<scm>
</parent>
<artifactId>features-protocol-framework</artifactId>
<version>${protocol-framework.version}</version>
- <packaging>pom</packaging>
+ <packaging>jar</packaging>
<properties>
<features.file>features.xml</features.file>
<artifactId>features-config</artifactId>
<classifier>features</classifier>
<type>xml</type>
- <scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>protocol-framework</artifactId>
</dependency>
+ <!-- test to validate features.xml -->
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>features-test</artifactId>
+ </dependency>
</dependencies>
<build>
</execution>
</executions>
</plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <systemPropertyVariables>
+ <karaf.distro.groupId>org.opendaylight.controller</karaf.distro.groupId>
+ <karaf.distro.artifactId>opendaylight-karaf-empty</karaf.distro.artifactId>
+ <karaf.distro.version>${commons.opendaylight.version}</karaf.distro.version>
+ </systemPropertyVariables>
+ <dependenciesToScan>
+ <dependency>org.opendaylight.yangtools:features-test</dependency>
+ </dependenciesToScan>
+ </configuration>
+ </plugin>
</plugins>
</build>
<scm>
<artifactId>toaster-config</artifactId>
<version>${mdsal.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>features-test</artifactId>
+ <version>${yangtools.version}</version>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>features-yangtools</artifactId>
<artifactId>binding-generator-impl</artifactId>
<version>${yangtools.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>binding-data-codec</artifactId>
+ <version>${yangtools.version}</version>
+ </dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>binding-generator-spi</artifactId>
public static final String SERVICE_QNAME_KEY = "serviceQName";
public static final String INSTANCE_NAME_KEY = "instanceName";
public static final String TYPE_KEY = ConfigRegistryConstants.TYPE_KEY;
- public static final String TYPE_CONFIG_REGISTRY = ConfigRegistryConstants.TYPE_CONFIG_REGISTRY;
public static final String TYPE_CONFIG_TRANSACTION = "ConfigTransaction";
public static final String TYPE_MODULE = "Module";
public static final String TYPE_SERVICE_REFERENCE = "ServiceReference";
public static final String TRANSACTION_NAME_KEY = "TransactionName";
public static final String REF_NAME_KEY = "RefName";
private static final String REPLACED_QUOTATION_MARK = "\\?";
+ public static final String ON_WILDCARD = "*";
public static ObjectName createON(String on) {
try {
public static ObjectName createModulePattern(String moduleName,
String instanceName) {
- if (moduleName == null) {
- moduleName = "*";
- }
- if (instanceName == null) {
- instanceName = "*";
- }
+ moduleName = moduleName == null ? ON_WILDCARD : moduleName;
+ instanceName = instanceName == null ? ON_WILDCARD : instanceName;
+
// do not return object names containing transaction name
ObjectName namePattern = ObjectNameUtil
.createON(ObjectNameUtil.ON_DOMAIN + ":"
public static ObjectName createModulePattern(String ifcName,
String instanceName, String transactionName) {
+ ifcName = ifcName == null ? ON_WILDCARD : ifcName;
+ instanceName = instanceName == null ? ON_WILDCARD : instanceName;
+ transactionName = transactionName == null ? ON_WILDCARD : transactionName;
+
return ObjectNameUtil.createON(ObjectNameUtil.ON_DOMAIN
+ ":type=Module," + ObjectNameUtil.MODULE_FACTORY_NAME_KEY
+ "=" + ifcName + "," + ObjectNameUtil.INSTANCE_NAME_KEY + "="
public static ObjectName createRuntimeBeanPattern(String moduleName,
String instanceName) {
+ moduleName = moduleName == null ? ON_WILDCARD : moduleName;
+ instanceName = instanceName == null ? ON_WILDCARD : instanceName;
+
return ObjectNameUtil.createON(ObjectNameUtil.ON_DOMAIN + ":"
+ ObjectNameUtil.TYPE_KEY + "="
+ ObjectNameUtil.TYPE_RUNTIME_BEAN + ","
--- /dev/null
+package org.opendaylight.controller.config.api;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.common.collect.Lists;
+import org.junit.Before;
+import org.junit.Test;
+
+public class JmxAttributeValidationExceptionTest {
+
+ private JmxAttribute jmxAttribute = new JmxAttribute("attr1");
+ private JmxAttribute jmxAttribute2 = new JmxAttribute("attr2");
+
+ @Before
+ public void setUp() throws Exception {
+
+ }
+
+ @Test
+ public void testGetAttributeNames() throws Exception {
+
+ }
+
+ @Test
+ public void testCheckNotNull() throws Exception {
+ try {
+ JmxAttributeValidationException.checkNotNull(false, "message", jmxAttribute);
+ } catch (JmxAttributeValidationException e) {
+ assertJmxEx(e, jmxAttribute.getAttributeName() + " " + "message", jmxAttribute);
+ }
+ }
+
+ @Test
+ public void testWrap() throws Exception {
+
+ }
+
+ @Test
+ public void testCheckCondition() throws Exception {
+ try {
+ JmxAttributeValidationException.checkCondition(false, "message", jmxAttribute);
+ } catch (JmxAttributeValidationException e) {
+ assertJmxEx(e, jmxAttribute.getAttributeName() + " " + "message", jmxAttribute);
+ }
+ }
+
+ private void assertJmxEx(JmxAttributeValidationException e, String message, JmxAttribute... attrNames) {
+ assertEquals(message, e.getMessage());
+ assertEquals(Lists.newArrayList(attrNames), e.getAttributeNames());
+ }
+}
\ No newline at end of file
--- /dev/null
+package org.opendaylight.controller.config.api;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
+import static org.junit.Assert.assertThat;
+import static org.junit.matchers.JUnitMatchers.containsString;
+
+import com.google.common.collect.Lists;
+import java.util.Map;
+import org.junit.Test;
+
+public class ValidationExceptionTest {
+
+ private String instance = "instance";
+ private final ModuleIdentifier mi = new ModuleIdentifier("module", instance);
+ private String instance2 = "instance2";
+ private final ModuleIdentifier mi2 = new ModuleIdentifier("module", instance2);
+ private final String message = "ex message";
+ private final Exception e = new IllegalStateException(message);
+
+ @Test
+ public void testCreateFromCollectedValidationExceptions() throws Exception {
+ ValidationException single = ValidationException.createForSingleException(mi, e);
+ ValidationException single2 = ValidationException.createForSingleException(mi2, e);
+
+ ValidationException collected = ValidationException.createFromCollectedValidationExceptions(Lists.newArrayList(single, single2));
+
+ Map<String, Map<String, ValidationException.ExceptionMessageWithStackTrace>> failedMap = collected.getFailedValidations();
+ assertEquals(1, failedMap.size());
+ assertTrue(failedMap.containsKey("module"));
+
+ Map<String, ValidationException.ExceptionMessageWithStackTrace> failedModule = failedMap.get("module");
+ assertEquals(2, failedModule.size());
+ assertTrue(failedModule.containsKey(instance));
+ assertEquals(message, failedModule.get(instance).getMessage());
+ assertEquals(message, failedModule.get(instance2).getMessage());
+ assertEquals(failedModule.get(instance), failedModule.get(instance2));
+ }
+
+ @Test
+ public void testCreateFromCollectedValidationExceptionsWithDuplicate() throws Exception {
+ ValidationException single = ValidationException.createForSingleException(mi, e);
+ ValidationException single2 = ValidationException.createForSingleException(mi, e);
+ try {
+ ValidationException.createFromCollectedValidationExceptions(Lists.newArrayList(single, single2));
+ } catch (IllegalArgumentException ex) {
+ // Duplicate exception
+ assertThat(ex.getMessage(), containsString("Cannot merge"));
+ return;
+ }
+ fail("Duplicate exception should have failed");
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * 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.api.jmx;
+
+import static junit.framework.Assert.fail;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.base.Throwables;
+import com.google.common.collect.Maps;
+import java.util.Map;
+import javax.management.ObjectName;
+import junit.framework.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+
+public class ObjectNameUtilTest {
+
+ private String moduleName;
+ private String instanceName;
+
+ @Before
+ public void setUp() throws Exception {
+ moduleName = "module";
+ instanceName = "instance";
+ }
+
+ @Test
+ public void testServiceReferenceName() throws Exception {
+ String serviceQName = "(namespace?revision=r)qname";
+ String refName = "refName";
+ String transaction = "transaction";
+
+ ObjectName serviceReferenceON = ObjectNameUtil.createTransactionServiceON(transaction, serviceQName, refName);
+ ObjectNameUtil.checkType(serviceReferenceON, ObjectNameUtil.TYPE_SERVICE_REFERENCE);
+
+ assertFalse(serviceReferenceON.isPattern());
+ assertEquals(serviceQName, ObjectNameUtil.getServiceQName(serviceReferenceON));
+ assertEquals(refName, ObjectNameUtil.getReferenceName(serviceReferenceON));
+ assertEquals(transaction, ObjectNameUtil.getTransactionName(serviceReferenceON));
+ assertEquals(ObjectNameUtil.createReadOnlyServiceON(serviceQName, refName), ObjectNameUtil.withoutTransactionName(serviceReferenceON));
+
+ serviceReferenceON = ObjectNameUtil.createReadOnlyServiceON(serviceQName, refName);
+ assertFalse(serviceReferenceON.isPattern());
+ assertEquals(serviceQName, ObjectNameUtil.getServiceQName(serviceReferenceON));
+ assertEquals(refName, ObjectNameUtil.getReferenceName(serviceReferenceON));
+ assertEquals(null, ObjectNameUtil.getTransactionName(serviceReferenceON));
+ }
+
+ @Test
+ public void testModuleName() throws Exception {
+ String txName = "transaction";
+
+ ObjectName on = ObjectNameUtil.createTransactionModuleON(txName, moduleName, instanceName);
+
+ ObjectNameUtil.checkDomain(on);
+ ObjectNameUtil.checkType(on, ObjectNameUtil.TYPE_MODULE);
+
+ assertFalse(on.isPattern());
+ assertEquals(moduleName, ObjectNameUtil.getFactoryName(on));
+ assertEquals(instanceName, ObjectNameUtil.getInstanceName(on));
+ assertEquals(txName, ObjectNameUtil.getTransactionName(on));
+ assertEquals(4, ObjectNameUtil.getAdditionalProperties(on).size());
+
+ ObjectName withoutTx = ObjectNameUtil.withoutTransactionName(on);
+ assertEquals(ObjectNameUtil.createReadOnlyModuleON(moduleName, instanceName), withoutTx);
+ assertEquals(moduleName, ObjectNameUtil.getFactoryName(withoutTx));
+ assertEquals(instanceName, ObjectNameUtil.getInstanceName(withoutTx));
+ assertEquals(null, ObjectNameUtil.getTransactionName(withoutTx));
+ assertEquals(on, ObjectNameUtil.withTransactionName(withoutTx, txName));
+
+ ObjectName pattern = ObjectNameUtil.createModulePattern(moduleName, null);
+ assertPattern(withoutTx, pattern);
+ pattern = ObjectNameUtil.createModulePattern(moduleName, null, txName);
+ assertPattern(on, pattern);
+ }
+
+ private void assertPattern(ObjectName test, ObjectName pattern) {
+ assertTrue(pattern.isPattern());
+ assertTrue(pattern.apply(test));
+ }
+
+ @Test
+ public void testRuntimeBeanName() throws Exception {
+
+ Map<String, String> properties = Maps.newHashMap();
+ properties.put("p1", "value");
+ properties.put("p2", "value2");
+
+ ObjectName on = ObjectNameUtil.createRuntimeBeanName(moduleName, instanceName, properties);
+
+ ObjectNameUtil.checkDomain(on);
+ ObjectNameUtil.checkTypeOneOf(on, ObjectNameUtil.TYPE_RUNTIME_BEAN);
+
+ assertFalse(on.isPattern());
+ assertEquals(moduleName, ObjectNameUtil.getFactoryName(on));
+ assertEquals(instanceName, ObjectNameUtil.getInstanceName(on));
+ assertEquals(2, ObjectNameUtil.getAdditionalPropertiesOfRuntimeBeanName(on).size());
+ assertTrue(ObjectNameUtil.getAdditionalPropertiesOfRuntimeBeanName(on).containsKey("p1"));
+ assertEquals("value", ObjectNameUtil.getAdditionalPropertiesOfRuntimeBeanName(on).get("p1"));
+ assertTrue(ObjectNameUtil.getAdditionalProperties(on).containsKey("p2"));
+ assertEquals("value2", ObjectNameUtil.getAdditionalPropertiesOfRuntimeBeanName(on).get("p2"));
+
+ ObjectName pattern = ObjectNameUtil.createRuntimeBeanPattern(null, instanceName);
+ assertPattern(on, pattern);
+ }
+
+ @Test
+ public void testModuleIdentifier() throws Exception {
+ ModuleIdentifier mi = new ModuleIdentifier(moduleName, instanceName);
+ ObjectName on = ObjectNameUtil.createReadOnlyModuleON(mi);
+ assertEquals(moduleName, ObjectNameUtil.getFactoryName(on));
+ assertEquals(instanceName, ObjectNameUtil.getInstanceName(on));
+
+ assertEquals(mi, ObjectNameUtil.fromON(on, ObjectNameUtil.TYPE_MODULE));
+ }
+
+ @Test
+ public void testChecks() throws Exception {
+ final ObjectName on = ObjectNameUtil.createON("customDomain", ObjectNameUtil.TYPE_KEY, ObjectNameUtil.TYPE_MODULE);
+
+ assertFailure(new Runnable() {
+ @Override
+ public void run() {
+ ObjectNameUtil.checkTypeOneOf(on, ObjectNameUtil.TYPE_RUNTIME_BEAN, ObjectNameUtil.TYPE_CONFIG_TRANSACTION);
+ }
+ }, IllegalArgumentException.class);
+
+ assertFailure(new Runnable() {
+ @Override
+ public void run() {
+ ObjectNameUtil.checkType(on, ObjectNameUtil.TYPE_RUNTIME_BEAN);
+ }
+ }, IllegalArgumentException.class);
+
+ assertFailure(new Runnable() {
+ @Override
+ public void run() {
+ ObjectNameUtil.checkDomain(on);
+ }
+ }, IllegalArgumentException.class);
+ }
+
+ private void assertFailure(Runnable test, Class<? extends Exception> ex) {
+ try {
+ test.run();
+ } catch(Exception e) {
+ Assert.assertTrue("Failed with wrong exception: " + Throwables.getStackTraceAsString(e),
+ e.getClass().isAssignableFrom(ex));
+ return;
+ }
+
+ fail(test + " should have failed on " + ex);
+ }
+}
*/
package org.opendaylight.controller.config.manager.impl.osgi;
+import com.google.common.annotations.VisibleForTesting;
+import javax.management.ObjectName;
import org.opendaylight.controller.config.api.ConflictingVersionException;
import org.opendaylight.controller.config.api.ValidationException;
import org.opendaylight.controller.config.api.jmx.CommitStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import javax.management.ObjectName;
-
/**
* Every time factory is added or removed, blank transaction is triggered to handle
* {@link org.opendaylight.controller.config.spi.ModuleFactory#getDefaultModules(org.opendaylight.controller.config.api.DependencyResolverFactory, org.osgi.framework.BundleContext)}
public class BlankTransactionServiceTracker implements ServiceTrackerCustomizer<ModuleFactory, Object> {
private static final Logger logger = LoggerFactory.getLogger(BlankTransactionServiceTracker.class);
- private final ConfigRegistryImpl configRegistry;
+ public static final int DEFAULT_MAX_ATTEMPTS = 10;
- public BlankTransactionServiceTracker(ConfigRegistryImpl configRegistry) {
- this.configRegistry = configRegistry;
+ private final BlankTransaction blankTransaction;
+ private int maxAttempts;
+
+ public BlankTransactionServiceTracker(final ConfigRegistryImpl configRegistry) {
+ this(new BlankTransaction() {
+ @Override
+ public CommitStatus hit() throws ValidationException, ConflictingVersionException {
+ ObjectName tx = configRegistry.beginConfig(true);
+ return configRegistry.commitConfig(tx);
+ }
+ });
+ }
+
+ public BlankTransactionServiceTracker(final BlankTransaction blankTransaction) {
+ this(blankTransaction, DEFAULT_MAX_ATTEMPTS);
+ }
+
+ @VisibleForTesting
+ BlankTransactionServiceTracker(final BlankTransaction blankTx, final int maxAttempts) {
+ this.blankTransaction = blankTx;
+ this.maxAttempts = maxAttempts;
}
@Override
synchronized void blankTransaction() {
// race condition check: config-persister might push new configuration while server is starting up.
ConflictingVersionException lastException = null;
- int maxAttempts = 10;
for (int i = 0; i < maxAttempts; i++) {
try {
// create transaction
- boolean blankTransaction = true;
- ObjectName tx = configRegistry.beginConfig(blankTransaction);
- CommitStatus commitStatus = configRegistry.commitConfig(tx);
+ CommitStatus commitStatus = blankTransaction.hit();
logger.debug("Committed blank transaction with status {}", commitStatus);
return;
} catch (ConflictingVersionException e) {
public void removedService(ServiceReference<ModuleFactory> moduleFactoryServiceReference, Object o) {
blankTransaction();
}
+
+ @VisibleForTesting
+ static interface BlankTransaction {
+ CommitStatus hit() throws ValidationException, ConflictingVersionException;
+ }
}
import static java.lang.String.format;
+import com.google.common.annotations.VisibleForTesting;
import java.io.InputStream;
import java.net.URL;
import java.util.List;
blankTransactionServiceTracker.blankTransaction();
}
- // TODO:test
- private static ServiceRegistration<?> registerFactory(String factoryClassName, Bundle bundle) {
+ @VisibleForTesting
+ protected static ServiceRegistration<?> registerFactory(String factoryClassName, Bundle bundle) {
String errorMessage;
+ Exception ex = null;
try {
Class<?> clazz = bundle.loadClass(factoryClassName);
if (ModuleFactory.class.isAssignableFrom(clazz)) {
errorMessage = logMessage(
"Could not instantiate {} in bundle {}, reason {}",
factoryClassName, bundle, e);
+ ex = e;
} catch (IllegalAccessException e) {
errorMessage = logMessage(
- "Illegal access during instatiation of class {} in bundle {}, reason {}",
+ "Illegal access during instantiation of class {} in bundle {}, reason {}",
factoryClassName, bundle, e);
+ ex = e;
}
} else {
errorMessage = logMessage(
}
} catch (ClassNotFoundException e) {
errorMessage = logMessage(
- "Could not find class {} in bunde {}, reason {}",
+ "Could not find class {} in bundle {}, reason {}",
factoryClassName, bundle, e);
+ ex = e;
}
- throw new IllegalStateException(errorMessage);
+
+ throw ex == null ? new IllegalStateException(errorMessage) : new IllegalStateException(errorMessage, ex);
}
public static String logMessage(String slfMessage, Object... params) {
*/
public static Set<Class<?>> getOsgiRegistrationTypes(
Class<? extends Module> configBeanClass) {
- // TODO test with service interface hierarchy
Set<Class<?>> serviceInterfaces = getServiceInterfaces(configBeanClass);
Set<Class<?>> result = new HashSet<>();
for (Class<?> clazz : serviceInterfaces) {
import org.opendaylight.controller.config.spi.ModuleFactory;
import org.opendaylight.yangtools.yang.binding.annotations.ModuleQName;
+import org.opendaylight.yangtools.yang.common.QName;
import org.osgi.framework.BundleContext;
import java.util.HashSet;
inspected = inspected.getSuperclass();
}
if (annotation != null) {
- // FIXME
- String qName = "(" + annotation.namespace() + "?revision=" + annotation.revision() + ")" + annotation.name();
- result.add(qName);
+ result.add(QName.create(annotation.namespace(), annotation.revision(), annotation.name()).toString());
}
}
return result;
package org.opendaylight.controller.config.manager.impl.util;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ListIterator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import org.osgi.util.tracker.BundleTracker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.ListIterator;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
public class OsgiRegistrationUtil {
private static final Logger logger = LoggerFactory.getLogger(OsgiRegistrationUtil.class);
--- /dev/null
+package org.opendaylight.controller.config.manager.impl.osgi;
+
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertSame;
+import static junit.framework.Assert.fail;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import java.util.Collections;
+import javax.management.ObjectName;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.config.api.ConflictingVersionException;
+import org.opendaylight.controller.config.api.ModuleIdentifier;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.config.api.jmx.CommitStatus;
+import org.opendaylight.controller.config.spi.ModuleFactory;
+import org.osgi.framework.ServiceReference;
+
+public class BlankTransactionServiceTrackerTest {
+
+ @Mock
+ private BlankTransactionServiceTracker.BlankTransaction blankTx;
+ private BlankTransactionServiceTracker tracker;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ doReturn(new CommitStatus(Collections.<ObjectName>emptyList(), Collections.<ObjectName>emptyList(), Collections.<ObjectName>emptyList())).when(blankTx).hit();
+ tracker = new BlankTransactionServiceTracker(blankTx);
+ }
+
+ @Test
+ public void testBlankTransaction() throws Exception {
+ tracker.addingService(getMockServiceReference());
+ tracker.modifiedService(getMockServiceReference(), null);
+ tracker.removedService(getMockServiceReference(), null);
+ verify(blankTx, times(3)).hit();
+ }
+
+ @Test
+ public void testValidationException() throws Exception {
+ IllegalArgumentException argumentException = new IllegalArgumentException();
+ ValidationException validationException = ValidationException.createForSingleException(new ModuleIdentifier("m", "i"), argumentException);
+ doThrow(validationException).when(blankTx).hit();
+ try {
+ tracker.addingService(getMockServiceReference());
+ } catch (Exception e) {
+ verify(blankTx, times(1)).hit();
+ assertNotNull(e.getCause());
+ assertSame(validationException, e.getCause());
+ return;
+ }
+
+ fail("Exception should have occurred for validation exception");
+ }
+
+ @Test
+ public void testConflictingException() throws Exception {
+ int maxAttempts = 2;
+ tracker = new BlankTransactionServiceTracker(blankTx, maxAttempts);
+
+ final ConflictingVersionException ex = new ConflictingVersionException();
+ doThrow(ex).when(blankTx).hit();
+ try {
+ tracker.addingService(getMockServiceReference());
+ } catch (Exception e) {
+ verify(blankTx, times(maxAttempts)).hit();
+ return;
+ }
+
+ fail("Exception should have occurred for conflicting exception");
+ }
+
+ private ServiceReference<ModuleFactory> getMockServiceReference() {
+ return mock(ServiceReference.class);
+ }
+}
--- /dev/null
+package org.opendaylight.controller.config.manager.impl.osgi;
+
+import static junit.framework.Assert.fail;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.matchers.JUnitMatchers.containsString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import com.google.common.collect.Lists;
+import java.util.Map;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.opendaylight.controller.config.spi.ModuleFactory;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+
+public class BundleContextBackedModuleFactoriesResolverTest {
+
+ @Mock
+ private BundleContext bundleContext;
+ private BundleContextBackedModuleFactoriesResolver resolver;
+ private ServiceReference s1;
+ private ServiceReference s2;
+ private ModuleFactory f1;
+ private ModuleFactory f2;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ s1 = getServiceRef();
+ s2 = getServiceRef();
+ doReturn(Lists.newArrayList(s1, s2)).when(bundleContext).getServiceReferences(ModuleFactory.class, null);
+ f1 = getMockFactory("f1");
+ doReturn(f1).when(bundleContext).getService(s1);
+ f2 = getMockFactory("f2");
+ doReturn(f2).when(bundleContext).getService(s2);
+ resolver = new BundleContextBackedModuleFactoriesResolver(bundleContext);
+ }
+
+ private ModuleFactory getMockFactory(String name) {
+ ModuleFactory mock = mock(ModuleFactory.class);
+ doReturn(name).when(mock).toString();
+ doReturn(name).when(mock).getImplementationName();
+ return mock;
+ }
+
+ private ServiceReference getServiceRef() {
+ ServiceReference mock = mock(ServiceReference.class);
+ doReturn("serviceRef").when(mock).toString();
+ final Bundle bundle = mock(Bundle.class);
+ doReturn(bundleContext).when(bundle).getBundleContext();
+ doReturn(bundle).when(mock).getBundle();
+ return mock;
+ }
+
+ @Test
+ public void testGetAllFactories() throws Exception {
+ Map<String, Map.Entry<ModuleFactory, BundleContext>> allFactories = resolver.getAllFactories();
+ assertEquals(2, allFactories.size());
+ assertTrue(allFactories.containsKey(f1.getImplementationName()));
+ assertEquals(f1, allFactories.get(f1.getImplementationName()).getKey());
+ assertEquals(bundleContext, allFactories.get(f1.getImplementationName()).getValue());
+ assertTrue(allFactories.containsKey(f2.getImplementationName()));
+ assertEquals(f2, allFactories.get(f2.getImplementationName()).getKey());
+ assertEquals(bundleContext, allFactories.get(f2.getImplementationName()).getValue());
+ }
+
+ @Test
+ public void testDuplicateFactories() throws Exception {
+ doReturn(f1).when(bundleContext).getService(s2);
+ try {
+ resolver.getAllFactories();
+ } catch (Exception e) {
+ assertThat(e.getMessage(), containsString(f1.getImplementationName()));
+ assertThat(e.getMessage(), containsString("unique"));
+ return;
+ }
+
+ fail("Should fail with duplicate factory name");
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testNullFactory() throws Exception {
+ doReturn(null).when(bundleContext).getService(s2);
+ resolver.getAllFactories();
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testNullFactoryName() throws Exception {
+ doReturn(null).when(f1).getImplementationName();
+ resolver.getAllFactories();
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testNullBundleName() throws Exception {
+ doReturn(null).when(s1).getBundle();
+ resolver.getAllFactories();
+ }
+}
--- /dev/null
+package org.opendaylight.controller.config.manager.impl.osgi;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import com.google.common.util.concurrent.Futures;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.util.tracker.BundleTrackerCustomizer;
+
+public class ExtensibleBundleTrackerTest {
+
+ @Mock
+ private BundleContext bundleContext;
+ @Mock
+ private Bundle bundle;
+ @Mock
+ private BundleEvent bundleEvent;
+
+ @Mock
+ private BundleTrackerCustomizer<Object> primaryTracker;
+ @Mock
+ private BundleTrackerCustomizer<?> additionalTracker;
+
+ private ExtensibleBundleTracker<Object> extensibleBundleTracker;
+ private Object primaryValue = new Object();
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ doReturn("bundle").when(bundle).toString();
+ doReturn("bundleEvent").when(bundleEvent).toString();
+
+ doReturn(primaryValue).when(primaryTracker).addingBundle(bundle, bundleEvent);
+ doNothing().when(primaryTracker).modifiedBundle(bundle, bundleEvent, primaryValue);
+ doNothing().when(primaryTracker).removedBundle(bundle, bundleEvent, primaryValue);
+
+ doReturn(new Object()).when(additionalTracker).addingBundle(bundle, bundleEvent);
+ doNothing().when(additionalTracker).modifiedBundle(bundle, bundleEvent, null);
+ doNothing().when(additionalTracker).removedBundle(bundle, bundleEvent, null);
+ extensibleBundleTracker = new ExtensibleBundleTracker<>(bundleContext, primaryTracker, additionalTracker);
+ }
+
+ @Test
+ public void testAddingBundle() throws Exception {
+ assertEquals(primaryValue, extensibleBundleTracker.addingBundle(bundle, bundleEvent).get());
+ InOrder inOrder = Mockito.inOrder(primaryTracker, additionalTracker);
+ inOrder.verify(primaryTracker).addingBundle(bundle, bundleEvent);
+ inOrder.verify(additionalTracker).addingBundle(bundle, bundleEvent);
+ }
+
+ @Test
+ public void testRemovedBundle() throws Exception {
+ extensibleBundleTracker.removedBundle(bundle, bundleEvent, Futures.immediateFuture(primaryValue));
+ InOrder inOrder = Mockito.inOrder(primaryTracker, additionalTracker);
+ inOrder.verify(primaryTracker).removedBundle(bundle, bundleEvent, primaryValue);
+ inOrder.verify(additionalTracker).removedBundle(bundle, bundleEvent, null);
+ }
+
+ @Test
+ public void testRemovedBundleWithEx() throws Exception {
+ IllegalStateException throwable = new IllegalStateException();
+ extensibleBundleTracker.removedBundle(bundle, bundleEvent, Futures.immediateFailedFuture(throwable));
+ verifyZeroInteractions(primaryTracker);
+ verifyZeroInteractions(additionalTracker);
+ }
+}
--- /dev/null
+package org.opendaylight.controller.config.manager.impl.osgi;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+
+import java.util.Dictionary;
+import java.util.Set;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.opendaylight.controller.config.api.DependencyResolver;
+import org.opendaylight.controller.config.api.DependencyResolverFactory;
+import org.opendaylight.controller.config.api.DynamicMBeanWithInstance;
+import org.opendaylight.controller.config.api.annotations.AbstractServiceInterface;
+import org.opendaylight.controller.config.spi.Module;
+import org.opendaylight.controller.config.spi.ModuleFactory;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.ServiceRegistration;
+
+public class ModuleFactoryBundleTrackerTest {
+
+ @Mock
+ private Bundle bundle;
+ @Mock
+ private BundleContext context;
+ @Mock
+ private ServiceRegistration<?> reg;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ doAnswer(new Answer() {
+ @Override
+ public Object answer(final InvocationOnMock invocation) throws Throwable {
+ return getClass().getClassLoader().loadClass((String) invocation.getArguments()[0]);
+ }
+ }).when(bundle).loadClass(anyString());
+ doReturn("mockBundle").when(bundle).toString();
+ doReturn(context).when(bundle).getBundleContext();
+ doReturn(reg).when(context).registerService(anyString(), anyObject(), any(Dictionary.class));
+ }
+
+ @Test
+ public void testRegisterFactory() throws Exception {
+ ModuleFactoryBundleTracker.registerFactory(TestingFactory.class.getName(), bundle);
+ verify(context).registerService(ModuleFactory.class.getName(), TestingFactory.currentInstance, null);
+ }
+
+ @Test
+ public void testRegisterFactoryInstantiateEx() throws Exception {
+ try {
+ ModuleFactoryBundleTracker.registerFactory(WrongConstructorTestingFactory.class.getName(), bundle);
+ } catch (Exception e) {
+ verifyZeroInteractions(context);
+ assertNotNull(e.getCause());
+ assertEquals(InstantiationException.class, e.getCause().getClass());
+ return;
+ }
+
+ fail("Cannot register without proper constructor");
+ }
+
+ @Test
+ public void testRegisterFactoryInstantiateExAccess() throws Exception {
+ try {
+ ModuleFactoryBundleTracker.registerFactory(NoAccessConstructorTestingFactory.class.getName(), bundle);
+ } catch (Exception e) {
+ verifyZeroInteractions(context);
+ assertNotNull(e.getCause());
+ assertEquals(IllegalAccessException.class, e.getCause().getClass());
+ return;
+ }
+
+ fail("Cannot register without proper constructor");
+ }
+
+ @Test
+ public void testRegisterFactoryNotExtending() throws Exception {
+ try {
+ ModuleFactoryBundleTracker.registerFactory(NotExtendingTestingFactory.class.getName(), bundle);
+ } catch (Exception e) {
+ verifyZeroInteractions(context);
+ return;
+ }
+
+ fail("Cannot register without extend");
+ }
+
+ @Test
+ public void testRegisterFactoryNotExisting() throws Exception {
+ try {
+ ModuleFactoryBundleTracker.registerFactory("Unknown class", bundle);
+ } catch (Exception e) {
+ verifyZeroInteractions(context);
+ assertNotNull(e.getCause());
+ assertEquals(ClassNotFoundException.class, e.getCause().getClass());
+ return;
+ }
+
+ fail("Cannot register without extend");
+ }
+
+ @Mock
+ private BlankTransactionServiceTracker blankTxTracker;
+
+ @Test
+ public void testAddingBundle() throws Exception {
+ final ModuleFactoryBundleTracker tracker = new ModuleFactoryBundleTracker(blankTxTracker);
+ doReturn(getClass().getResource("/module-factories/module-factory-ok")).when(bundle).getEntry(anyString());
+ tracker.addingBundle(bundle, mock(BundleEvent.class));
+ verify(context).registerService(ModuleFactory.class.getName(), TestingFactory.currentInstance, null);
+ }
+
+ @Test
+ public void testAddingBundleError() throws Exception {
+ final ModuleFactoryBundleTracker tracker = new ModuleFactoryBundleTracker(blankTxTracker);
+ doReturn(getClass().getResource("/module-factories/module-factory-fail")).when(bundle).getEntry(anyString());
+ try {
+ tracker.addingBundle(bundle, mock(BundleEvent.class));
+ } catch (Exception e) {
+ verifyZeroInteractions(context);
+ return;
+ }
+
+ fail("Cannot register");
+ }
+
+ static class WrongConstructorTestingFactory extends TestingFactory {
+ WrongConstructorTestingFactory(String randomParam) {
+ }
+ }
+
+ static class NotExtendingTestingFactory {}
+
+ static class NoAccessConstructorTestingFactory extends TestingFactory {
+ private NoAccessConstructorTestingFactory() {
+ }
+ }
+
+ static class TestingFactory implements ModuleFactory {
+
+ static TestingFactory currentInstance;
+
+ TestingFactory() {
+ currentInstance = this;
+ }
+
+ @Override
+ public String getImplementationName() {
+ return "Testing";
+ }
+
+ @Override
+ public Module createModule(final String instanceName, final DependencyResolver dependencyResolver, final BundleContext bundleContext) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Module createModule(final String instanceName, final DependencyResolver dependencyResolver, final DynamicMBeanWithInstance old, final BundleContext bundleContext) throws Exception {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isModuleImplementingServiceInterface(final Class<? extends AbstractServiceInterface> serviceInterface) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Set<Class<? extends AbstractServiceInterface>> getImplementedServiceIntefaces() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Set<? extends Module> getDefaultModules(final DependencyResolverFactory dependencyResolverFactory, final BundleContext bundleContext) {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
import static org.junit.Assert.assertEquals;
+import com.google.common.collect.Sets;
+import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
-
import javax.management.MXBean;
-
import org.junit.Test;
import org.opendaylight.controller.config.api.annotations.AbstractServiceInterface;
+import org.opendaylight.controller.config.api.annotations.ServiceInterfaceAnnotation;
import org.opendaylight.controller.config.manager.testingservices.seviceinterface.TestingScheduledThreadPoolServiceInterface;
import org.opendaylight.controller.config.manager.testingservices.seviceinterface.TestingThreadPoolServiceInterface;
import org.opendaylight.controller.config.spi.Module;
import org.opendaylight.yangtools.concepts.Identifiable;
-import com.google.common.collect.Sets;
-
public class InterfacesHelperTest {
interface SuperA {
}
+ @ServiceInterfaceAnnotation(value = "a", osgiRegistrationType = SuperA.class, namespace = "n", revision = "r", localName = "l")
+ interface Service extends AbstractServiceInterface{}
+ @ServiceInterfaceAnnotation(value = "b", osgiRegistrationType = SuperC.class, namespace = "n", revision = "r", localName = "l")
+ interface SubService extends Service{}
+
abstract class SubClass extends SuperClass implements SubA, Module {
}
+ abstract class SubClassWithService implements SubService, Module {
+
+ }
+
@Test
public void testGetAllInterfaces() {
Set<Class<?>> expected = Sets.<Class<?>> newHashSet(SuperA.class, SuperBMXBean.class, SuperC.class,
InterfacesHelper.getAllInterfaces(SubClass.class));
}
+ @Test
+ public void testGetServiceInterfaces() throws Exception {
+ assertEquals(Collections.<Class<?>>emptySet(), InterfacesHelper.getServiceInterfaces(SubClass.class));
+ assertEquals(Sets.<Class<?>>newHashSet(Service.class, SubService.class), InterfacesHelper.getServiceInterfaces(SubClassWithService.class));
+ }
+
+ @Test
+ public void testGetOsgiRegistrationTypes() throws Exception {
+ assertEquals(Collections.<Class<?>>emptySet(), InterfacesHelper.getOsgiRegistrationTypes(SubClass.class));
+ assertEquals(Sets.<Class<?>>newHashSet(SuperA.class, SuperC.class),
+ InterfacesHelper.getOsgiRegistrationTypes(SubClassWithService.class));
+ }
+
@Test
public void testGetMXInterfaces() {
Set<Class<?>> expected = Sets.<Class<?>> newHashSet(SuperBMXBean.class, SubA.class);
+++ /dev/null
-/*
- * 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.manager.impl.util;
-
-import com.google.common.base.Throwables;
-import com.google.common.collect.Sets;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
-import org.opendaylight.controller.config.manager.impl.AbstractLockedPlatformMBeanServerTest;
-
-import javax.management.ObjectName;
-import java.util.Set;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-
-public class ObjectNameUtilTest extends AbstractLockedPlatformMBeanServerTest {
- private Set<ObjectName> unregisterONs;
-
- @Before
- public void initUnregisterList() {
- unregisterONs = Sets.newHashSet();
- }
-
- @After
- public void unregisterONs() {
- Exception lastException = null;
- for (ObjectName on : unregisterONs) {
- try {
- platformMBeanServer.unregisterMBean(on);
- } catch (Exception e) {
- lastException = e;
- }
- }
- if (lastException != null) {
- throw Throwables.propagate(lastException);
- }
- }
-
- @Test
- public void testQuotation() throws Exception {
- String serviceQName = "(namespace?revision=r)qname";
- String refName = "refName";
- String transaction = "transaction";
- ObjectName serviceReferenceON = ObjectNameUtil.createTransactionServiceON(transaction, serviceQName, refName);
- assertFalse(serviceReferenceON.isPattern());
- assertEquals(serviceQName, ObjectNameUtil.getServiceQName(serviceReferenceON));
- assertEquals(refName, ObjectNameUtil.getReferenceName(serviceReferenceON));
- assertEquals(transaction, ObjectNameUtil.getTransactionName(serviceReferenceON));
-
- serviceReferenceON = ObjectNameUtil.createReadOnlyServiceON(serviceQName, refName);
- assertFalse(serviceReferenceON.isPattern());
- assertEquals(serviceQName, ObjectNameUtil.getServiceQName(serviceReferenceON));
- assertEquals(refName, ObjectNameUtil.getReferenceName(serviceReferenceON));
- assertEquals(null, ObjectNameUtil.getTransactionName(serviceReferenceON));
-
- }
-}
--- /dev/null
+package org.opendaylight.controller.config.manager.impl.util;
+
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import org.junit.Test;
+import org.mockito.InOrder;
+import org.mockito.Mockito;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.util.tracker.BundleTracker;
+import org.osgi.util.tracker.ServiceTracker;
+
+public class OsgiRegistrationUtilTest {
+
+ @Test
+ public void testRegisterService() throws Exception {
+ final BundleContext bundleContext = mock(BundleContext.class);
+ ServiceRegistration<?> registration = mockServiceRegistration();
+ doReturn(registration).when(bundleContext).registerService(String.class, "string", null);
+ ServiceRegistration<?> registration2 = mockServiceRegistration();
+ doReturn(registration2).when(bundleContext).registerService(Object.class, "string", null);
+
+ AutoCloseable aggregatedRegister = OsgiRegistrationUtil.registerService(bundleContext, "string", String.class, Object.class);
+ aggregatedRegister.close();
+
+ InOrder inOrder = Mockito.inOrder(registration, registration2);
+ inOrder.verify(registration2).unregister();
+ inOrder.verify(registration).unregister();
+ }
+
+ @Test
+ public void testWrap() throws Exception {
+ final ServiceRegistration<?> serviceReg = mockServiceRegistration();
+ OsgiRegistrationUtil.wrap(serviceReg).close();
+ verify(serviceReg).unregister();
+
+ final BundleTracker tracker = mock(BundleTracker.class);
+ doNothing().when(tracker).close();
+ OsgiRegistrationUtil.wrap(tracker).close();
+ verify(tracker).close();
+
+ final ServiceTracker<?, ?> sTracker = mock(ServiceTracker.class);
+ doNothing().when(sTracker).close();
+ OsgiRegistrationUtil.wrap(sTracker).close();
+ verify(sTracker).close();
+ }
+
+ private ServiceRegistration<?> mockServiceRegistration() {
+ ServiceRegistration mock = mock(ServiceRegistration.class);
+ doNothing().when(mock).unregister();
+ return mock;
+ }
+
+ @Test
+ public void testAggregate() throws Exception {
+
+ }
+}
\ No newline at end of file
import org.opendaylight.yangtools.yang.binding.annotations.ModuleQName;
-@ModuleQName(namespace = "namespace", revision = "revision", name = "name")
+@ModuleQName(namespace = "namespace", revision = "2012-12-12", name = "name")
public abstract class AbstractTestingFixedThreadPoolModuleFactory {
}
@Test
public void testQNames() {
Set<String> availableModuleFactoryQNames = configRegistryClient.getAvailableModuleFactoryQNames();
- String expected = "(namespace?revision=revision)name";
+ String expected = "(namespace?revision=2012-12-12)name";
+
assertEquals(Sets.newHashSet(expected), availableModuleFactoryQNames);
}
--- /dev/null
+org.opendaylight.controller.config.manager.impl.osgi.ModuleFactoryBundleTrackerTest$NotExtendingTestingFactory
\ No newline at end of file
--- /dev/null
+org.opendaylight.controller.config.manager.impl.osgi.ModuleFactoryBundleTrackerTest$TestingFactory
\ No newline at end of file
<groupId>${project.groupId}</groupId>
<artifactId>netconf-client</artifactId>
</dependency>
+
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>config-manager</artifactId>
+ <type>test-jar</type>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>config-manager</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>config-util</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netty-threadgroup-config</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netty-timer-config</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>mockito-configuration</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
--- /dev/null
+package org.opendaylight.controller.config.yang.config.netconf.client.dispatcher;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.ObjectName;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.config.api.ConflictingVersionException;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.config.api.jmx.CommitStatus;
+import org.opendaylight.controller.config.manager.impl.AbstractConfigTest;
+import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver;
+import org.opendaylight.controller.config.util.ConfigTransactionJMXClient;
+import org.opendaylight.controller.config.yang.netty.threadgroup.NettyThreadgroupModuleFactory;
+import org.opendaylight.controller.config.yang.netty.threadgroup.NettyThreadgroupModuleMXBean;
+import org.opendaylight.controller.config.yang.netty.timer.HashedWheelTimerModuleFactory;
+
+public class NetconfClientDispatcherModuleTest extends AbstractConfigTest{
+
+ private NetconfClientDispatcherModuleFactory factory;
+ private final String instanceName = "dispatch";
+
+ @Before
+ public void setUp() {
+ factory = new NetconfClientDispatcherModuleFactory();
+ super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(mockedContext,factory,
+ new NettyThreadgroupModuleFactory(),
+ new HashedWheelTimerModuleFactory()));
+ }
+
+ @Test
+ public void testCreateBean() throws InstanceAlreadyExistsException, ValidationException, ConflictingVersionException {
+ ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
+
+ createInstance(transaction, instanceName, "timer", "thGroup");
+ createInstance(transaction, instanceName + 2, "timer2", "thGroup2");
+ transaction.validateConfig();
+ CommitStatus status = transaction.commit();
+
+ assertBeanCount(2, factory.getImplementationName());
+ assertStatus(status, 2 + 4, 0, 0);
+ }
+
+ @Test
+ public void testReusingOldInstance() throws InstanceAlreadyExistsException, ConflictingVersionException, ValidationException {
+
+ ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
+ createInstance(transaction, instanceName, "timer", "thGroup");
+
+ transaction.commit();
+
+ transaction = configRegistryClient.createTransaction();
+ assertBeanCount(1, factory.getImplementationName());
+ CommitStatus status = transaction.commit();
+
+ assertBeanCount(1, factory.getImplementationName());
+ assertStatus(status, 0, 0, 3);
+ }
+
+ @Test
+ public void testReconfigure() throws InstanceAlreadyExistsException, ConflictingVersionException,
+ ValidationException, InstanceNotFoundException {
+
+ ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
+ createInstance(transaction, instanceName, "timer", "thGroup");
+
+ transaction.commit();
+
+ transaction = configRegistryClient.createTransaction();
+ assertBeanCount(1, factory.getImplementationName());
+ NetconfClientDispatcherModuleMXBean mxBean = transaction.newMBeanProxy(
+ transaction.lookupConfigBean(NetconfClientDispatcherModuleFactory.NAME, instanceName),
+ NetconfClientDispatcherModuleMXBean.class);
+ mxBean.setBossThreadGroup(getThreadGroup(transaction, "group2"));
+ CommitStatus status = transaction.commit();
+
+ assertBeanCount(1, factory.getImplementationName());
+ assertStatus(status, 1, 1, 2);
+ }
+
+ private ObjectName createInstance(ConfigTransactionJMXClient transaction, String instanceName, String timerName, String threadGroupName)
+ throws InstanceAlreadyExistsException {
+ ObjectName nameCreated = transaction.createModule(factory.getImplementationName(), instanceName);
+ NetconfClientDispatcherModuleMXBean mxBean = transaction.newMBeanProxy(nameCreated, NetconfClientDispatcherModuleMXBean.class);
+ ObjectName thGroup = getThreadGroup(transaction, threadGroupName);
+ mxBean.setBossThreadGroup(thGroup);
+ mxBean.setWorkerThreadGroup(thGroup);
+ mxBean.setTimer(getTimer(transaction, timerName));
+ return nameCreated;
+ }
+
+ private ObjectName getTimer(ConfigTransactionJMXClient transaction, String name) throws InstanceAlreadyExistsException {
+ return transaction.createModule(HashedWheelTimerModuleFactory.NAME, name);
+ }
+
+ private ObjectName getThreadGroup(ConfigTransactionJMXClient transaction, String name) throws InstanceAlreadyExistsException {
+ ObjectName nameCreated = transaction.createModule(NettyThreadgroupModuleFactory.NAME, name);
+ NettyThreadgroupModuleMXBean mxBean = transaction.newMXBeanProxy(nameCreated, NettyThreadgroupModuleMXBean.class);
+ mxBean.setThreadCount(1);
+ return nameCreated;
+ }
+}
--- /dev/null
+/*
+ * 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.netty.eventexecutor;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.ObjectName;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.controller.config.api.ConflictingVersionException;
+import org.opendaylight.controller.config.api.ValidationException;
+import org.opendaylight.controller.config.api.jmx.CommitStatus;
+import org.opendaylight.controller.config.manager.impl.AbstractConfigTest;
+import org.opendaylight.controller.config.manager.impl.factoriesresolver.HardcodedModuleFactoriesResolver;
+import org.opendaylight.controller.config.util.ConfigTransactionJMXClient;
+
+public class ImmediateEventExecutorModuleTest extends AbstractConfigTest {
+
+ private GlobalEventExecutorModuleFactory factory;
+ private final String instanceName = ImmediateEventExecutorModuleFactory.SINGLETON_NAME;
+
+ @Before
+ public void setUp() {
+ factory = new GlobalEventExecutorModuleFactory();
+ super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(mockedContext,factory));
+ }
+
+ @Test
+ public void testCreateBean() throws InstanceAlreadyExistsException, ValidationException,
+ ConflictingVersionException {
+ ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
+
+ createInstance(transaction, instanceName);
+
+ transaction.validateConfig();
+ CommitStatus status = transaction.commit();
+
+ assertBeanCount(1, factory.getImplementationName());
+ assertStatus(status, 1, 0, 0);
+ }
+
+ @Test
+ public void testReusingOldInstance() throws InstanceAlreadyExistsException, ConflictingVersionException,
+ ValidationException {
+
+ ConfigTransactionJMXClient transaction = configRegistryClient.createTransaction();
+ createInstance(transaction, instanceName);
+
+ transaction.commit();
+
+ transaction = configRegistryClient.createTransaction();
+ assertBeanCount(1, factory.getImplementationName());
+ CommitStatus status = transaction.commit();
+
+ assertBeanCount(1, factory.getImplementationName());
+ assertStatus(status, 0, 0, 1);
+ }
+
+ private ObjectName createInstance(ConfigTransactionJMXClient transaction, String instanceName)
+ throws InstanceAlreadyExistsException {
+ ObjectName nameCreated = transaction.createModule(factory.getImplementationName(), instanceName);
+ transaction.newMBeanProxy(nameCreated, ImmediateEventExecutorModuleMXBean.class);
+ return nameCreated;
+ }
+
+}
public void setUp() {
factory = new HashedWheelTimerModuleFactory();
threadFactory = new NamingThreadFactoryModuleFactory();
- super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(factory, threadFactory));
+ super.initConfigTransactionManagerImpl(new HardcodedModuleFactoriesResolver(mockedContext, factory, threadFactory));
}
public void testValidationExceptionTickDuration() throws InstanceAlreadyExistsException {
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>commons.opendaylight</artifactId>
+ <version>1.4.2-SNAPSHOT</version>
+ <relativePath>../../commons/opendaylight</relativePath>
+ </parent>
+ <artifactId>opendaylight-karaf-empty</artifactId>
+ <packaging>pom</packaging>
+ <prerequisites>
+ <maven>3.0</maven>
+ </prerequisites>
+
+ <dependencies>
+ <dependency>
+ <!-- scope is compile so all features (there is only one) are installed
+ into startup.properties and the feature repo itself is not installed -->
+ <groupId>org.apache.karaf.features</groupId>
+ <artifactId>framework</artifactId>
+ <version>${karaf.version}</version>
+ <type>kar</type>
+ </dependency>
+ <!-- scope is runtime so the feature repo is listed in the features
+ service config file, and features may be installed using the
+ karaf-maven-plugin configuration -->
+ <dependency>
+ <groupId>org.apache.karaf.features</groupId>
+ <artifactId>standard</artifactId>
+ <version>${karaf.version}</version>
+ <classifier>features</classifier>
+ <type>xml</type>
+ <scope>runtime</scope>
+ </dependency>
+
+ <!-- ODL Branding -->
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>karaf.branding</artifactId>
+ <scope>compile</scope>
+ </dependency>
+
+ <!-- Resources needed -->
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>opendaylight-karaf-resources</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.eclipse.m2e</groupId>
+ <artifactId>lifecycle-mapping</artifactId>
+ <version>1.0.0</version>
+ <configuration>
+ <lifecycleMappingMetadata>
+ <pluginExecutions>
+ <pluginExecution>
+ <pluginExecutionFilter>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <versionRange>[0,)</versionRange>
+ <goals>
+ <goal>cleanVersions</goal>
+ </goals>
+ </pluginExecutionFilter>
+ <action>
+ <ignore></ignore>
+ </action>
+ </pluginExecution>
+ <pluginExecution>
+ <pluginExecutionFilter>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <versionRange>[0,)</versionRange>
+ <goals>
+ <goal>copy</goal>
+ <goal>unpack</goal>
+ </goals>
+ </pluginExecutionFilter>
+ <action>
+ <ignore></ignore>
+ </action>
+ </pluginExecution>
+ <pluginExecution>
+ <pluginExecutionFilter>
+ <groupId>org.apache.karaf.tooling</groupId>
+ <artifactId>karaf-maven-plugin</artifactId>
+ <versionRange>[0,)</versionRange>
+ <goals>
+ <goal>commands-generate-help</goal>
+ </goals>
+ </pluginExecutionFilter>
+ <action>
+ <ignore></ignore>
+ </action>
+ </pluginExecution>
+ <pluginExecution>
+ <pluginExecutionFilter>
+ <groupId>org.fusesource.scalate</groupId>
+ <artifactId>maven-scalate-plugin</artifactId>
+ <versionRange>[0,)</versionRange>
+ <goals>
+ <goal>sitegen</goal>
+ </goals>
+ </pluginExecutionFilter>
+ <action>
+ <ignore></ignore>
+ </action>
+ </pluginExecution>
+ <pluginExecution>
+ <pluginExecutionFilter>
+ <groupId>org.apache.servicemix.tooling</groupId>
+ <artifactId>depends-maven-plugin</artifactId>
+ <versionRange>[0,)</versionRange>
+ <goals>
+ <goal>generate-depends-file</goal>
+ </goals>
+ </pluginExecutionFilter>
+ <action>
+ <ignore></ignore>
+ </action>
+ </pluginExecution>
+ </pluginExecutions>
+ </lifecycleMappingMetadata>
+ </configuration>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.karaf.tooling</groupId>
+ <artifactId>karaf-maven-plugin</artifactId>
+ <version>${karaf.version}</version>
+ <extensions>true</extensions>
+ <executions>
+ <execution>
+ <id>process-resources</id>
+ <goals>
+ <goal>install-kars</goal>
+ </goals>
+ <phase>process-resources</phase>
+ </execution>
+ <execution>
+ <id>package</id>
+ <goals>
+ <goal>instance-create-archive</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-checkstyle-plugin</artifactId>
+ <version>${checkstyle.version}</version>
+ <configuration>
+ <excludes>**\/target\/,**\/bin\/,**\/target-ide\/,**\/configuration\/initial\/</excludes>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <version>2.6</version>
+ <executions>
+ <execution>
+ <id>copy</id>
+ <goals>
+ <goal>copy</goal>
+ </goals>
+ <!-- here the phase you need -->
+ <phase>generate-resources</phase>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>karaf.branding</artifactId>
+ <version>${karaf.branding.version}</version>
+ <outputDirectory>target/assembly/lib</outputDirectory>
+ <destFileName>karaf.branding-${branding.version}.jar</destFileName>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </execution>
+ <execution>
+ <id>unpack-karaf-resources</id>
+ <goals>
+ <goal>unpack-dependencies</goal>
+ </goals>
+ <phase>prepare-package</phase>
+ <configuration>
+ <outputDirectory>${project.build.directory}/assembly</outputDirectory>
+ <groupId>org.opendaylight.controller</groupId>
+ <includeArtifactIds>opendaylight-karaf-resources</includeArtifactIds>
+ <excludes>META-INF\/**</excludes>
+ <excludeTransitive>true</excludeTransitive>
+ <ignorePermissions>false</ignorePermissions>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>prepare-package</phase>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ <configuration>
+ <tasks>
+ <chmod perm="755">
+ <fileset dir="${project.build.directory}/assembly/bin">
+ <include name="karaf"/>
+ <include name="instance"/>
+ </fileset>
+ </chmod>
+ </tasks>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ <scm>
+ <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
+ <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
+ <tag>HEAD</tag>
+ <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:Main</url>
+ </scm>
+</project>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2014 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>commons.opendaylight</artifactId>
+ <version>1.4.2-SNAPSHOT</version>
+ <relativePath>../../commons/opendaylight</relativePath>
+ </parent>
+ <artifactId>opendaylight-karaf-resources</artifactId>
+ <description>Resources for opendaylight-karaf</description>
+ <packaging>jar</packaging>
+</project>
<version>${karaf.version}</version>
<type>kar</type>
</dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>base-features</artifactId>
- <version>${project.version}</version>
- <type>kar</type>
- </dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>karaf.branding</artifactId>
<scope>compile</scope>
</dependency>
- <!-- scope is runtime so the feature repo is listed in the features
- service config file, and features may be installed using the
- karaf-maven-plugin configuration -->
- <dependency>
- <groupId>org.apache.karaf.features</groupId>
- <artifactId>standard</artifactId>
- <version>${karaf.version}</version>
- <classifier>features</classifier>
- <type>xml</type>
- <scope>runtime</scope>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>base-features</artifactId>
- <version>${project.parent.version}</version>
- <classifier>features</classifier>
- <type>xml</type>
- <scope>runtime</scope>
- </dependency>
- <!-- scope is compile so all features (there is only one) are installed
- into startup.properties and the feature repo itself is not installed -->
+
+ <!-- Resources needed -->
<dependency>
<groupId>org.opendaylight.controller</groupId>
- <artifactId>extras-features</artifactId>
+ <artifactId>opendaylight-karaf-resources</artifactId>
<version>${project.version}</version>
- <type>kar</type>
- <scope>runtime</scope>
- </dependency>
- <!-- AD-SAL Related Features -->
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>features-adsal</artifactId>
- <classifier>features</classifier>
- <type>xml</type>
- <scope>runtime</scope>
</dependency>
+
+ <!-- scope is not runtime so the feature repo is pulled into the local
+ repo on build and thus you actually run. Failure to do so can lead
+ to very confusing errors for devs -->
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>features-nsf</artifactId>
- <version>${project.version}</version>
+ <groupId>org.apache.karaf.features</groupId>
+ <artifactId>standard</artifactId>
+ <version>${karaf.version}</version>
<classifier>features</classifier>
<type>xml</type>
- <scope>runtime</scope>
</dependency>
+ <!--
+ controller provided features:
+ Note: Nothing should go here that is not locked
+ down with testing... ie, no broken feature repos
+ -->
+
<!-- MD-SAL Related Features -->
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>features-mdsal</artifactId>
<classifier>features</classifier>
<type>xml</type>
- <scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>features-flow</artifactId>
<classifier>features</classifier>
<type>xml</type>
- <scope>runtime</scope>
</dependency>
</dependencies>
</artifactItems>
</configuration>
</execution>
+ <execution>
+ <id>unpack-karaf-resources</id>
+ <goals>
+ <goal>unpack-dependencies</goal>
+ </goals>
+ <phase>prepare-package</phase>
+ <configuration>
+ <outputDirectory>${project.build.directory}/assembly</outputDirectory>
+ <groupId>org.opendaylight.controller</groupId>
+ <includeArtifactIds>opendaylight-karaf-resources</includeArtifactIds>
+ <excludes>META-INF\/**</excludes>
+ <excludeTransitive>true</excludeTransitive>
+ <ignorePermissions>false</ignorePermissions>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>prepare-package</phase>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ <configuration>
+ <tasks>
+ <chmod perm="755">
+ <fileset dir="${project.build.directory}/assembly/bin">
+ <include name="karaf"/>
+ <include name="instance"/>
+ </fileset>
+ </chmod>
+ </tasks>
+ </configuration>
+ </execution>
</executions>
</plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-antrun-plugin</artifactId>
- <executions>
- <execution>
- <phase>prepare-package</phase>
- <goals>
- <goal>run</goal>
- </goals>
- <configuration>
- <tasks>
- <copy todir="${project.build.directory}/assembly/bin" overwrite="true">
- <fileset dir="${basedir}/src/main/resources/karaf/" includes="karaf,karaf.bat,instance,instance.bat"/>
- </copy>
- </tasks>
- </configuration>
- </execution>
- </executions>
- </plugin>
</plugins>
</build>
<scm>
+++ /dev/null
-################################################################################
-#
-# 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.
-#
-################################################################################
-
-#
-# Java platform package export properties.
-#
-
-# Standard package set. Note that:
-# - javax.transaction* is exported with a mandatory attribute
-jre-1.6= \
- javax.accessibility, \
- javax.activation;version="1.1", \
- javax.activity, \
- javax.crypto, \
- javax.crypto.interfaces, \
- javax.crypto.spec, \
- javax.imageio, \
- javax.imageio.event, \
- javax.imageio.metadata, \
- javax.imageio.plugins.bmp, \
- javax.imageio.plugins.jpeg, \
- javax.imageio.spi, \
- javax.imageio.stream, \
- javax.jws, \
- javax.jws.soap, \
- javax.lang.model, \
- javax.lang.model.element, \
- javax.lang.model.type, \
- javax.lang.model.util, \
- javax.management, \
- javax.management.loading, \
- javax.management.modelmbean, \
- javax.management.monitor, \
- javax.management.openmbean, \
- javax.management.relation, \
- javax.management.remote, \
- javax.management.remote.rmi, \
- javax.management.timer, \
- javax.naming, \
- javax.naming.directory, \
- javax.naming.event, \
- javax.naming.ldap, \
- javax.naming.spi, \
- javax.net, \
- javax.net.ssl, \
- javax.print, \
- javax.print.attribute, \
- javax.print.attribute.standard, \
- javax.print.event, \
- javax.rmi, \
- javax.rmi.CORBA, \
- javax.rmi.ssl, \
- javax.script, \
- javax.security.auth, \
- javax.security.auth.callback, \
- javax.security.auth.kerberos, \
- javax.security.auth.login, \
- javax.security.auth.spi, \
- javax.security.auth.x500, \
- javax.security.cert, \
- javax.security.sasl, \
- javax.sound.midi, \
- javax.sound.midi.spi, \
- javax.sound.sampled, \
- javax.sound.sampled.spi, \
- javax.sql, \
- javax.sql.rowset, \
- javax.sql.rowset.serial, \
- javax.sql.rowset.spi, \
- javax.swing, \
- javax.swing.border, \
- javax.swing.colorchooser, \
- javax.swing.event, \
- javax.swing.filechooser, \
- javax.swing.plaf, \
- javax.swing.plaf.basic, \
- javax.swing.plaf.metal, \
- javax.swing.plaf.multi, \
- javax.swing.plaf.synth, \
- javax.swing.table, \
- javax.swing.text, \
- javax.swing.text.html, \
- javax.swing.text.html.parser, \
- javax.swing.text.rtf, \
- javax.swing.tree, \
- javax.swing.undo, \
- javax.tools, \
- javax.transaction; javax.transaction.xa; partial=true; mandatory:=partial, \
- javax.xml, \
- javax.xml.bind;version="2.2.1", \
- javax.xml.bind.annotation;version="2.2.1", \
- javax.xml.bind.annotation.adapters;version="2.2.1", \
- javax.xml.bind.attachment;version="2.2.1", \
- javax.xml.bind.helpers;version="2.2.1", \
- javax.xml.bind.util;version="2.2.1", \
- javax.xml.crypto, \
- javax.xml.crypto.dom, \
- javax.xml.crypto.dsig, \
- javax.xml.crypto.dsig.dom, \
- javax.xml.crypto.dsig.keyinfo, \
- javax.xml.crypto.dsig.spec, \
- javax.xml.datatype, \
- javax.xml.namespace, \
- javax.xml.parsers, \
- javax.xml.soap;version="1.3", \
- javax.xml.stream;version="1.2", \
- javax.xml.stream.events;version="1.2", \
- javax.xml.stream.util;version="1.2", \
- javax.xml.transform, \
- javax.xml.transform.dom, \
- javax.xml.transform.sax, \
- javax.xml.transform.stax, \
- javax.xml.transform.stream, \
- javax.xml.validation, \
- javax.xml.ws;version="2.2", \
- javax.xml.ws.handler;version="2.2", \
- javax.xml.ws.handler.soap;version="2.2", \
- javax.xml.ws.http;version="2.2", \
- javax.xml.ws.soap;version="2.2", \
- javax.xml.ws.spi;version="2.2", \
- javax.xml.ws.wsaddressing;version="2.2", \
- javax.xml.ws.spi.http;version="2.2", \
- javax.xml.xpath, \
- org.ietf.jgss, \
- org.omg.CORBA, \
- org.omg.CORBA_2_3, \
- org.omg.CORBA_2_3.portable, \
- org.omg.CORBA.DynAnyPackage, \
- org.omg.CORBA.ORBPackage, \
- org.omg.CORBA.portable, \
- org.omg.CORBA.TypeCodePackage, \
- org.omg.CosNaming, \
- org.omg.CosNaming.NamingContextExtPackage, \
- org.omg.CosNaming.NamingContextPackage, \
- org.omg.Dynamic, \
- org.omg.DynamicAny, \
- org.omg.DynamicAny.DynAnyFactoryPackage, \
- org.omg.DynamicAny.DynAnyPackage, \
- org.omg.IOP, \
- org.omg.IOP.CodecFactoryPackage, \
- org.omg.IOP.CodecPackage, \
- org.omg.Messaging, \
- org.omg.PortableInterceptor, \
- org.omg.PortableInterceptor.ORBInitInfoPackage, \
- org.omg.PortableServer, \
- org.omg.PortableServer.CurrentPackage, \
- org.omg.PortableServer.POAManagerPackage, \
- org.omg.PortableServer.POAPackage, \
- org.omg.PortableServer.portable, \
- org.omg.PortableServer.ServantLocatorPackage, \
- org.omg.SendingContext, \
- org.omg.stub.java.rmi, \
- org.omg.stub.javax.management.remote.rmi, \
- org.w3c.dom, \
- org.w3c.dom.bootstrap, \
- org.w3c.dom.css, \
- org.w3c.dom.events, \
- org.w3c.dom.html, \
- org.w3c.dom.ls, \
- org.w3c.dom.ranges, \
- org.w3c.dom.stylesheets, \
- org.w3c.dom.traversal, \
- org.w3c.dom.views, \
- org.w3c.dom.xpath, \
- org.xml.sax, \
- org.xml.sax.ext, \
- org.xml.sax.helpers, \
- javax.annotation.processing
-
-# Standard package set. Note that:
-# - javax.transaction* is exported with a mandatory attribute
-jre-1.7= \
- javax.accessibility, \
- javax.activation;version="1.1", \
- javax.activity, \
- javax.crypto, \
- javax.crypto.interfaces, \
- javax.crypto.spec, \
- javax.imageio, \
- javax.imageio.event, \
- javax.imageio.metadata, \
- javax.imageio.plugins.bmp, \
- javax.imageio.plugins.jpeg, \
- javax.imageio.spi, \
- javax.imageio.stream, \
- javax.jws, \
- javax.jws.soap, \
- javax.lang.model, \
- javax.lang.model.element, \
- javax.lang.model.type, \
- javax.lang.model.util, \
- javax.management, \
- javax.management.loading, \
- javax.management.modelmbean, \
- javax.management.monitor, \
- javax.management.openmbean, \
- javax.management.relation, \
- javax.management.remote, \
- javax.management.remote.rmi, \
- javax.management.timer, \
- javax.naming, \
- javax.naming.directory, \
- javax.naming.event, \
- javax.naming.ldap, \
- javax.naming.spi, \
- javax.net, \
- javax.net.ssl, \
- javax.print, \
- javax.print.attribute, \
- javax.print.attribute.standard, \
- javax.print.event, \
- javax.rmi, \
- javax.rmi.CORBA, \
- javax.rmi.ssl, \
- javax.script, \
- javax.security.auth, \
- javax.security.auth.callback, \
- javax.security.auth.kerberos, \
- javax.security.auth.login, \
- javax.security.auth.spi, \
- javax.security.auth.x500, \
- javax.security.cert, \
- javax.security.sasl, \
- javax.sound.midi, \
- javax.sound.midi.spi, \
- javax.sound.sampled, \
- javax.sound.sampled.spi, \
- javax.sql, \
- javax.sql.rowset, \
- javax.sql.rowset.serial, \
- javax.sql.rowset.spi, \
- javax.swing, \
- javax.swing.border, \
- javax.swing.colorchooser, \
- javax.swing.event, \
- javax.swing.filechooser, \
- javax.swing.plaf, \
- javax.swing.plaf.basic, \
- javax.swing.plaf.metal, \
- javax.swing.plaf.multi, \
- javax.swing.plaf.synth, \
- javax.swing.table, \
- javax.swing.text, \
- javax.swing.text.html, \
- javax.swing.text.html.parser, \
- javax.swing.text.rtf, \
- javax.swing.tree, \
- javax.swing.undo, \
- javax.tools, \
- javax.transaction; javax.transaction.xa; partial=true; mandatory:=partial, \
- javax.xml, \
- javax.xml.bind;version="2.2.1", \
- javax.xml.bind.annotation;version="2.2.1", \
- javax.xml.bind.annotation.adapters;version="2.2.1", \
- javax.xml.bind.attachment;version="2.2.1", \
- javax.xml.bind.helpers;version="2.2.1", \
- javax.xml.bind.util;version="2.2.1", \
- javax.xml.crypto, \
- javax.xml.crypto.dom, \
- javax.xml.crypto.dsig, \
- javax.xml.crypto.dsig.dom, \
- javax.xml.crypto.dsig.keyinfo, \
- javax.xml.crypto.dsig.spec, \
- javax.xml.datatype, \
- javax.xml.namespace, \
- javax.xml.parsers, \
- javax.xml.soap;version="1.3", \
- javax.xml.stream;version="1.2", \
- javax.xml.stream.events;version="1.2", \
- javax.xml.stream.util;version="1.2", \
- javax.xml.transform, \
- javax.xml.transform.dom, \
- javax.xml.transform.sax, \
- javax.xml.transform.stax, \
- javax.xml.transform.stream, \
- javax.xml.validation, \
- javax.xml.ws;version="2.2", \
- javax.xml.ws.handler;version="2.2", \
- javax.xml.ws.handler.soap;version="2.2", \
- javax.xml.ws.http;version="2.2", \
- javax.xml.ws.soap;version="2.2", \
- javax.xml.ws.spi;version="2.2", \
- javax.xml.ws.wsaddressing;version="2.2", \
- javax.xml.ws.spi.http;version="2.2", \
- javax.xml.xpath, \
- org.ietf.jgss, \
- org.omg.CORBA, \
- org.omg.CORBA_2_3, \
- org.omg.CORBA_2_3.portable, \
- org.omg.CORBA.DynAnyPackage, \
- org.omg.CORBA.ORBPackage, \
- org.omg.CORBA.portable, \
- org.omg.CORBA.TypeCodePackage, \
- org.omg.CosNaming, \
- org.omg.CosNaming.NamingContextExtPackage, \
- org.omg.CosNaming.NamingContextPackage, \
- org.omg.Dynamic, \
- org.omg.DynamicAny, \
- org.omg.DynamicAny.DynAnyFactoryPackage, \
- org.omg.DynamicAny.DynAnyPackage, \
- org.omg.IOP, \
- org.omg.IOP.CodecFactoryPackage, \
- org.omg.IOP.CodecPackage, \
- org.omg.Messaging, \
- org.omg.PortableInterceptor, \
- org.omg.PortableInterceptor.ORBInitInfoPackage, \
- org.omg.PortableServer, \
- org.omg.PortableServer.CurrentPackage, \
- org.omg.PortableServer.POAManagerPackage, \
- org.omg.PortableServer.POAPackage, \
- org.omg.PortableServer.portable, \
- org.omg.PortableServer.ServantLocatorPackage, \
- org.omg.SendingContext, \
- org.omg.stub.java.rmi, \
- org.omg.stub.javax.management.remote.rmi, \
- org.w3c.dom, \
- org.w3c.dom.bootstrap, \
- org.w3c.dom.css, \
- org.w3c.dom.events, \
- org.w3c.dom.html, \
- org.w3c.dom.ls, \
- org.w3c.dom.ranges, \
- org.w3c.dom.stylesheets, \
- org.w3c.dom.traversal, \
- org.w3c.dom.views, \
- org.w3c.dom.xpath, \
- org.xml.sax, \
- org.xml.sax.ext, \
- org.xml.sax.helpers, \
- javax.annotation.processing
-
-jre-1.8= \
- javax.accessibility, \
- javax.activation;version="1.1", \
- javax.activity, \
- javax.crypto, \
- javax.crypto.interfaces, \
- javax.crypto.spec, \
- javax.imageio, \
- javax.imageio.event, \
- javax.imageio.metadata, \
- javax.imageio.plugins.bmp, \
- javax.imageio.plugins.jpeg, \
- javax.imageio.spi, \
- javax.imageio.stream, \
- javax.jws, \
- javax.jws.soap, \
- javax.lang.model, \
- javax.lang.model.element, \
- javax.lang.model.type, \
- javax.lang.model.util, \
- javax.management, \
- javax.management.loading, \
- javax.management.modelmbean, \
- javax.management.monitor, \
- javax.management.openmbean, \
- javax.management.relation, \
- javax.management.remote, \
- javax.management.remote.rmi, \
- javax.management.timer, \
- javax.naming, \
- javax.naming.directory, \
- javax.naming.event, \
- javax.naming.ldap, \
- javax.naming.spi, \
- javax.net, \
- javax.net.ssl, \
- javax.print, \
- javax.print.attribute, \
- javax.print.attribute.standard, \
- javax.print.event, \
- javax.rmi, \
- javax.rmi.CORBA, \
- javax.rmi.ssl, \
- javax.script, \
- javax.security.auth, \
- javax.security.auth.callback, \
- javax.security.auth.kerberos, \
- javax.security.auth.login, \
- javax.security.auth.spi, \
- javax.security.auth.x500, \
- javax.security.cert, \
- javax.security.sasl, \
- javax.sound.midi, \
- javax.sound.midi.spi, \
- javax.sound.sampled, \
- javax.sound.sampled.spi, \
- javax.sql, \
- javax.sql.rowset, \
- javax.sql.rowset.serial, \
- javax.sql.rowset.spi, \
- javax.swing, \
- javax.swing.border, \
- javax.swing.colorchooser, \
- javax.swing.event, \
- javax.swing.filechooser, \
- javax.swing.plaf, \
- javax.swing.plaf.basic, \
- javax.swing.plaf.metal, \
- javax.swing.plaf.multi, \
- javax.swing.plaf.synth, \
- javax.swing.table, \
- javax.swing.text, \
- javax.swing.text.html, \
- javax.swing.text.html.parser, \
- javax.swing.text.rtf, \
- javax.swing.tree, \
- javax.swing.undo, \
- javax.tools, \
- javax.transaction; javax.transaction.xa; partial=true; mandatory:=partial, \
- javax.xml, \
- javax.xml.bind;version="2.2.1", \
- javax.xml.bind.annotation;version="2.2.1", \
- javax.xml.bind.annotation.adapters;version="2.2.1", \
- javax.xml.bind.attachment;version="2.2.1", \
- javax.xml.bind.helpers;version="2.2.1", \
- javax.xml.bind.util;version="2.2.1", \
- javax.xml.crypto, \
- javax.xml.crypto.dom, \
- javax.xml.crypto.dsig, \
- javax.xml.crypto.dsig.dom, \
- javax.xml.crypto.dsig.keyinfo, \
- javax.xml.crypto.dsig.spec, \
- javax.xml.datatype, \
- javax.xml.namespace, \
- javax.xml.parsers, \
- javax.xml.soap;version="1.3", \
- javax.xml.stream;version="1.2", \
- javax.xml.stream.events;version="1.2", \
- javax.xml.stream.util;version="1.2", \
- javax.xml.transform, \
- javax.xml.transform.dom, \
- javax.xml.transform.sax, \
- javax.xml.transform.stax, \
- javax.xml.transform.stream, \
- javax.xml.validation, \
- javax.xml.ws;version="2.2", \
- javax.xml.ws.handler;version="2.2", \
- javax.xml.ws.handler.soap;version="2.2", \
- javax.xml.ws.http;version="2.2", \
- javax.xml.ws.soap;version="2.2", \
- javax.xml.ws.spi;version="2.2", \
- javax.xml.ws.wsaddressing;version="2.2", \
- javax.xml.ws.spi.http;version="2.2", \
- javax.xml.xpath, \
- org.ietf.jgss, \
- org.omg.CORBA, \
- org.omg.CORBA_2_3, \
- org.omg.CORBA_2_3.portable, \
- org.omg.CORBA.DynAnyPackage, \
- org.omg.CORBA.ORBPackage, \
- org.omg.CORBA.portable, \
- org.omg.CORBA.TypeCodePackage, \
- org.omg.CosNaming, \
- org.omg.CosNaming.NamingContextExtPackage, \
- org.omg.CosNaming.NamingContextPackage, \
- org.omg.Dynamic, \
- org.omg.DynamicAny, \
- org.omg.DynamicAny.DynAnyFactoryPackage, \
- org.omg.DynamicAny.DynAnyPackage, \
- org.omg.IOP, \
- org.omg.IOP.CodecFactoryPackage, \
- org.omg.IOP.CodecPackage, \
- org.omg.Messaging, \
- org.omg.PortableInterceptor, \
- org.omg.PortableInterceptor.ORBInitInfoPackage, \
- org.omg.PortableServer, \
- org.omg.PortableServer.CurrentPackage, \
- org.omg.PortableServer.POAManagerPackage, \
- org.omg.PortableServer.POAPackage, \
- org.omg.PortableServer.portable, \
- org.omg.PortableServer.ServantLocatorPackage, \
- org.omg.SendingContext, \
- org.omg.stub.java.rmi, \
- org.omg.stub.javax.management.remote.rmi, \
- org.w3c.dom, \
- org.w3c.dom.bootstrap, \
- org.w3c.dom.css, \
- org.w3c.dom.events, \
- org.w3c.dom.html, \
- org.w3c.dom.ls, \
- org.w3c.dom.ranges, \
- org.w3c.dom.stylesheets, \
- org.w3c.dom.traversal, \
- org.w3c.dom.views, \
- org.w3c.dom.xpath, \
- org.xml.sax, \
- org.xml.sax.ext, \
- org.xml.sax.helpers, \
- javax.annotation.processing
+++ /dev/null
-#Bundles to be started on startup, with startlevel
-
-# feature: framework version: 3.0.1
-mvn\:org.ops4j.base/ops4j-base-lang/1.4.0 = 5
-mvn\:biz.aQute.bnd/bndlib/2.2.0 = 5
-mvn\:org.ops4j.pax.swissbox/pax-swissbox-bnd/1.7.0 = 5
-mvn\:org.ops4j.pax.url/pax-url-maven-commons/1.6.0 = 5
-mvn\:org.ops4j.pax.url/pax-url-aether/1.6.0 = 5
-mvn\:org.ops4j.pax.url/pax-url-wrap/1.6.0 = 5
-mvn\:javax.annotation/javax.annotation-api/1.2 = 5
-mvn\:org.ops4j.pax.logging/pax-logging-api/1.7.2 = 8
-mvn\:org.ops4j.pax.logging/pax-logging-service/1.7.2 = 8
-mvn\:org.apache.karaf.service/org.apache.karaf.service.guard/3.0.1 = 10
-mvn\:org.apache.felix/org.apache.felix.configadmin/1.6.0 = 10
-mvn\:org.apache.felix/org.apache.felix.fileinstall/3.2.8 = 11
-mvn\:org.ow2.asm/asm-all/4.1 = 12
-mvn\:org.apache.aries/org.apache.aries.util/1.1.0 = 20
-mvn\:org.apache.aries.proxy/org.apache.aries.proxy.api/1.0.0 = 20
-mvn\:org.apache.aries.proxy/org.apache.aries.proxy.impl/1.0.2 = 20
-mvn\:org.apache.aries.blueprint/org.apache.aries.blueprint.api/1.0.0 = 20
-mvn\:org.apache.aries.blueprint/org.apache.aries.blueprint.cm/1.0.3 = 20
-mvn\:org.apache.aries.blueprint/org.apache.aries.blueprint.core.compatibility/1.0.0 = 20
-mvn\:org.apache.aries.blueprint/org.apache.aries.blueprint.core/1.4.0 = 20
-mvn\:org.apache.karaf.deployer/org.apache.karaf.deployer.spring/3.0.1 = 24
-mvn\:org.apache.karaf.deployer/org.apache.karaf.deployer.blueprint/3.0.1 = 24
-mvn\:org.apache.karaf.deployer/org.apache.karaf.deployer.wrap/3.0.1 = 24
-mvn\:org.apache.karaf.region/org.apache.karaf.region.core/3.0.1 = 25
-mvn\:org.apache.karaf.features/org.apache.karaf.features.core/3.0.1 = 25
-mvn\:org.apache.karaf.deployer/org.apache.karaf.deployer.features/3.0.1 = 26
-mvn\:jline/jline/2.11 = 30
-mvn\:org.jledit/core/0.2.1 = 30
-mvn\:org.fusesource.jansi/jansi/1.11 = 30
-mvn\:org.ops4j.base/ops4j-base-util-property/1.4.0 = 30
-mvn\:org.ops4j.base/ops4j-base-util-xml/1.4.0 = 30
-mvn\:org.ops4j.base/ops4j-base-util-collections/1.4.0 = 30
-mvn\:org.ops4j.pax.url/pax-url-commons/1.6.0 = 30
-mvn\:org.ops4j.pax.swissbox/pax-swissbox-property/1.7.0 = 30
-mvn\:org.ops4j.base/ops4j-base-net/1.4.0 = 30
-mvn\:org.ops4j.base/ops4j-base-monitors/1.4.0 = 30
-mvn\:org.apache.karaf.features/org.apache.karaf.features.command/3.0.1 = 30
-mvn\:org.apache.karaf.shell/org.apache.karaf.shell.console/3.0.1 = 30
-mvn\:org.apache.karaf.jaas/org.apache.karaf.jaas.modules/3.0.1 = 30
-mvn\:org.apache.karaf.jaas/org.apache.karaf.jaas.config/3.0.1 = 30
-mvn\:org.apache.karaf.jaas/org.apache.karaf.jaas.boot/3.0.1 = 30
-mvn\:org.apache.sshd/sshd-core/0.9.0 = 30
-mvn\:org.apache.karaf.bundle/org.apache.karaf.bundle.command/3.0.1 = 30
-mvn\:org.apache.karaf.shell/org.apache.karaf.shell.table/3.0.1 = 30
-mvn\:org.apache.karaf.bundle/org.apache.karaf.bundle.core/3.0.1 = 30
-mvn\:org.apache.karaf.shell/org.apache.karaf.shell.help/3.0.1 = 30
-mvn\:org.apache.karaf.system/org.apache.karaf.system.core/3.0.1 = 30
-mvn\:org.apache.karaf.system/org.apache.karaf.system.command/3.0.1 = 30
-mvn\:org.apache.karaf.shell/org.apache.karaf.shell.commands/3.0.1 = 30
-mvn\:org.apache.aries.quiesce/org.apache.aries.quiesce.api/1.0.0 = 30
+++ /dev/null
-#
-# The properties defined in this file will be made available through system
-# properties at the very beginning of the Karaf's boot process.
-#
-
-# Use Equinox as default OSGi Framework Implementation
-karaf.framework=equinox
-
-# https://bugs.eclipse.org/bugs/show_bug.cgi?id=325578
-# Extend the framework to avoid the resources to be presented with
-# a URL of type bundleresource: but to be presented as file:
-osgi.hook.configurators.include=org.eclipse.virgo.kernel.equinox.extensions.hooks.ExtensionsHookConfigurator
-
-
-# Log level when the pax-logging service is not available
-# This level will only be used while the pax-logging service bundle
-# is not fully available.
-# To change log levels, please refer to the org.ops4j.pax.logging.cfg file
-# instead.
-org.ops4j.pax.logging.DefaultServiceLog.level = ERROR
-
-#
-# Name of this Karaf instance.
-#
-karaf.name = root
-
-#
-# Default repository where bundles will be loaded from before using
-# other Maven repositories. For the full Maven configuration, see
-# the org.ops4j.pax.url.mvn.cfg file.
-#
-karaf.default.repository = system
-
-#
-# Location of a shell script that will be run when starting a shell
-# session. This script can be used to create aliases and define
-# additional commands.
-#
-karaf.shell.init.script = ${karaf.etc}/shell.init.script
-
-#
-# Sets the maximum size of the shell command history. If not set,
-# defaults to 500 entries. Setting to 0 will disable history.
-#
-# karaf.shell.history.maxSize = 0
-
-#
-# Deletes the entire karaf.data directory at every start
-#
-karaf.clean.all = false
-
-#
-# Deletes the karaf.data/cache directory at every start
-#
-karaf.clean.cache = false
-
-#
-# Roles to use when logging into a local Karaf console.
-#
-# The syntax is the following:
-# [classname:]principal
-# where classname is the class name of the principal object
-# (defaults to org.apache.karaf.jaas.modules.RolePrincipal)
-# and principal is the name of the principal of that class
-# (defaults to instance).
-#
-karaf.local.roles = admin,manager,viewer
-
-#
-# Set this empty property to avoid errors when validating xml documents.
-#
-xml.catalog.files =
-
-#
-# Suppress the bell in the console when hitting backspace too many times
-# for example
-#
-jline.nobell = true
-
-#
-# ServiceMix specs options
-#
-org.apache.servicemix.specs.debug = false
-org.apache.servicemix.specs.timeout = 0
-
-#
-# Settings for the OSGi 4.3 Weaving
-# By default, we will not weave any classes. Change this setting to include classes
-# that you application needs to have woven.
-#
-org.apache.aries.proxy.weaving.enabled = none
-# Classes not to weave - Aries default + Xerces which is known to have issues.
-org.apache.aries.proxy.weaving.disabled = org.objectweb.asm.*,org.slf4j.*,org.apache.log4j.*,javax.*,org.apache.xerces.*
-
-#
-# By default, only Karaf shell commands are secured, but additional services can be
-# secured by expanding this filter
-#
-karaf.secured.services = (&(osgi.command.scope=*)(osgi.command.function=*))
-
-#
-# Security properties
-#
-# To enable OSGi security, uncomment the properties below,
-# install the framework-security feature and restart.
-#
-#java.security.policy=${karaf.etc}/all.policy
-#org.osgi.framework.security=osgi
-#org.osgi.framework.trust.repositories=${karaf.etc}/trustStore.ks
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>binding-generator-impl</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>binding-data-codec</artifactId>
+ </dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>binding-generator-spi</artifactId>
import java.util.HashSet;
import java.util.Map;
-import java.util.Set;
import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicLong;
-import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent;
-import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
+ * AbstractChangeListner implemented basic {@link AsyncDataChangeEvent} processing for
+ * flow node subDataObject (flows, groups and meters).
*
* @author <a href="mailto:vdemcak@cisco.com">Vaclav Demcak</a>
*
*/
public abstract class AbstractChangeListener implements DataChangeListener {
+ private final static Logger LOG = LoggerFactory.getLogger(AbstractChangeListener.class);
+
private final AtomicLong txNum = new AtomicLong();
private String transactionId;
@Override
- public void onDataChanged(DataChangeEvent<InstanceIdentifier<?>, DataObject> changeEvent) {
+ public void onDataChanged(final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> changeEvent) {
this.transactionId = this.newTransactionIdentifier().toString();
-
+ /* All DataObjects for create */
final Set<Entry<InstanceIdentifier<? extends DataObject>, DataObject>> createdEntries =
- changeEvent.getCreatedConfigurationData().entrySet();
- final Set<Entry<InstanceIdentifier<? extends DataObject>, DataObject>> updatedEntries =
- new HashSet<Entry<InstanceIdentifier<? extends DataObject>, DataObject>>();
-
+ changeEvent.getCreatedData().entrySet();
+ /* All DataObjects for updates - init HashSet */
+ final Set<Entry<InstanceIdentifier<? extends DataObject>, DataObject>> updatedEntries = new HashSet<>();
+ /* Filtered DataObject for update processing only */
Set<Entry<InstanceIdentifier<? extends DataObject>, DataObject>> updateConfigEntrySet =
- changeEvent.getUpdatedConfigurationData().entrySet();
+ changeEvent.getUpdatedData().entrySet();
updatedEntries.addAll(updateConfigEntrySet);
updatedEntries.removeAll(createdEntries);
-
+ /* All DataObjects for remove */
final Set<InstanceIdentifier<? extends DataObject>> removeEntriesInstanceIdentifiers =
- changeEvent.getRemovedConfigurationData();
-
+ changeEvent.getRemovedPaths();
+ /* Create DataObject processing (send to device) */
for (final Entry<InstanceIdentifier<? extends DataObject>, DataObject> createdEntry : createdEntries) {
- InstanceIdentifier<? extends DataObject> c_key = createdEntry.getKey();
- DataObject c_value = createdEntry.getValue();
- this.add(c_key, c_value);
+ InstanceIdentifier<? extends DataObject> entryKey = createdEntry.getKey();
+ DataObject entryValue = createdEntry.getValue();
+ if (preconditionForChange(entryKey, entryValue, null)) {
+ this.add(entryKey, entryValue);
+ }
}
for (final Entry<InstanceIdentifier<?>, DataObject> updatedEntrie : updatedEntries) {
Map<InstanceIdentifier<? extends DataObject>, DataObject> origConfigData =
- changeEvent.getOriginalConfigurationData();
-
- InstanceIdentifier<? extends Object> u_key = updatedEntrie.getKey();
- final DataObject originalFlow = origConfigData.get(u_key);
- final DataObject updatedFlow = updatedEntrie.getValue();
- this.update(u_key, originalFlow, updatedFlow);
+ changeEvent.getOriginalData();
+
+ InstanceIdentifier<? extends Object> entryKey = updatedEntrie.getKey();
+ final DataObject original = origConfigData.get(entryKey);
+ final DataObject updated = updatedEntrie.getValue();
+ if (preconditionForChange(entryKey, original, updated)) {
+ this.update(entryKey, original, updated);
+ }
}
for (final InstanceIdentifier<?> instanceId : removeEntriesInstanceIdentifiers) {
Map<InstanceIdentifier<? extends DataObject>, DataObject> origConfigData =
- changeEvent.getOriginalConfigurationData();
+ changeEvent.getOriginalData();
final DataObject removeValue = origConfigData.get(instanceId);
- this.remove(instanceId, removeValue);
+ if (preconditionForChange(instanceId, removeValue, null)) {
+ this.remove(instanceId, removeValue);
+ }
}
}
+ /**
+ * Method returns generated transaction ID, which is unique for
+ * every transaction. ID is composite from prefix ("DOM") and unique number.
+ *
+ * @return String transactionID
+ */
public String getTransactionId() {
return this.transactionId;
}
return "DOM-" + txNum.getAndIncrement();
}
- protected abstract void validate() throws IllegalStateException;
-
- protected abstract void remove(
+ /**
+ * Method check all local preconditions for apply relevant changes.
+ *
+ * @param InstanceIdentifier identifier - the whole path to DataObject
+ * @param DataObject original - original DataObject (for update)
+ * or relevant DataObject (add/delete operations)
+ * @param DataObject update - changed DataObject (contain updates)
+ * or should be null for (add/delete operations)
+ *
+ * @return boolean - applicable
+ */
+ protected abstract boolean preconditionForChange(
final InstanceIdentifier<? extends DataObject> identifier,
+ final DataObject original, final DataObject update);
+
+ /**
+ * Method checks the node data path in DataStore/OPERATIONAL because
+ * without the Node Identifier in DataStore/OPERATIONAL, device
+ * is not connected and device pre-configuration is allowed only.
+ *
+ * @param InstanceIdentifier identifier - could be whole path to DataObject,
+ * but parent Node.class InstanceIdentifier is used for a check only
+ *
+ * @return boolean - is the Node available in DataStore/OPERATIONAL (is connected)
+ */
+ protected boolean isNodeAvailable(final InstanceIdentifier<? extends DataObject> identifier,
+ final ReadOnlyTransaction readTrans) {
+ final InstanceIdentifier<Node> nodeInstanceId = identifier.firstIdentifierOf(Node.class);
+ try {
+ return readTrans.read(LogicalDatastoreType.OPERATIONAL, nodeInstanceId).get().isPresent();
+ }
+ catch (InterruptedException | ExecutionException e) {
+ LOG.error("Unexpected exception by reading Node ".concat(nodeInstanceId.toString()), e);
+ return false;
+ }
+ finally {
+ readTrans.close();
+ }
+ }
+
+ /**
+ * Method removes DataObject which is identified by InstanceIdentifier
+ * from device.
+ *
+ * @param InstanceIdentifier identifier - the whole path to DataObject
+ * @param DataObject remove - DataObject for removing
+ */
+ protected abstract void remove(final InstanceIdentifier<? extends DataObject> identifier,
final DataObject remove);
- protected abstract void update(
- final InstanceIdentifier<? extends DataObject> identifier,
+ /**
+ * Method updates the original DataObject to the update DataObject
+ * in device. Both are identified by same InstanceIdentifier
+ *
+ * @param InstanceIdentifier identifier - the whole path to DataObject
+ * @param DataObject original - original DataObject (for update)
+ * @param DataObject update - changed DataObject (contain updates)
+ */
+ protected abstract void update(final InstanceIdentifier<? extends DataObject> identifier,
final DataObject original, final DataObject update);
- protected abstract void add(
- final InstanceIdentifier<? extends DataObject> identifier,
+ /**
+ * Method adds the DataObject which is identified by InstanceIdentifier
+ * to device.
+ *
+ * @param InstanceIdentifier identifier - the whole path to new DataObject
+ * @param DataObject add - new DataObject
+ */
+ protected abstract void add(final InstanceIdentifier<? extends DataObject> identifier,
final DataObject add);
}
import org.opendaylight.controller.frm.flow.FlowProvider;
import org.opendaylight.controller.frm.group.GroupProvider;
import org.opendaylight.controller.frm.meter.MeterProvider;
+import org.opendaylight.controller.frm.reconil.FlowNodeReconcilProvider;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.sal.binding.api.AbstractBindingAwareProvider;
import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
-import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.SalGroupService;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.SalMeterService;
import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+/**
+ * Forwarding Rules Manager Activator
+ *
+ * Activator manages all Providers ({@link FlowProvider}, {@link GroupProvider},
+ * {@link MeterProvider} and the {@link FlowNodeReconcilProvider}).
+ * It registers all listeners (DataChangeEvent, ReconcilNotification)
+ * in the Session Initialization phase.
+ *
+ * @author <a href="mailto:vdemcak@cisco.com">Vaclav Demcak</a>
+ * *
+ */
public class FRMActivator extends AbstractBindingAwareProvider {
private final static Logger LOG = LoggerFactory.getLogger(FRMActivator.class);
- private static FlowProvider flowProvider = new FlowProvider();
- private static GroupProvider groupProvider = new GroupProvider();
- private static MeterProvider meterProvider = new MeterProvider();
+ private final FlowProvider flowProvider;
+ private final GroupProvider groupProvider;
+ private final MeterProvider meterProvider;
+ private final FlowNodeReconcilProvider flowNodeReconcilProvider;
+
+ public FRMActivator() {
+ this.flowProvider = new FlowProvider();
+ this.groupProvider = new GroupProvider();
+ this.meterProvider = new MeterProvider();
+ this.flowNodeReconcilProvider = new FlowNodeReconcilProvider();
+ }
@Override
public void onSessionInitiated(final ProviderContext session) {
- DataProviderService flowSalService = session.<DataProviderService>getSALService(DataProviderService.class);
- FRMActivator.flowProvider.setDataService(flowSalService);
- SalFlowService rpcFlowSalService = session.<SalFlowService>getRpcService(SalFlowService.class);
- FRMActivator.flowProvider.setSalFlowService(rpcFlowSalService);
- FRMActivator.flowProvider.start();
- DataProviderService groupSalService = session.<DataProviderService>getSALService(DataProviderService.class);
- FRMActivator.groupProvider.setDataService(groupSalService);
- SalGroupService rpcGroupSalService = session.<SalGroupService>getRpcService(SalGroupService.class);
- FRMActivator.groupProvider.setSalGroupService(rpcGroupSalService);
- FRMActivator.groupProvider.start();
- DataProviderService meterSalService = session.<DataProviderService>getSALService(DataProviderService.class);
- FRMActivator.meterProvider.setDataService(meterSalService);
- SalMeterService rpcMeterSalService = session.<SalMeterService>getRpcService(SalMeterService.class);
- FRMActivator.meterProvider.setSalMeterService(rpcMeterSalService);
- FRMActivator.meterProvider.start();
+ LOG.info("FRMActivator initialization.");
+ /* Flow */
+ try {
+ final DataBroker flowSalService = session.getSALService(DataBroker.class);
+ this.flowProvider.init(flowSalService);
+ this.flowProvider.start(session);
+ /* Group */
+ final DataBroker groupSalService = session.getSALService(DataBroker.class);
+ this.groupProvider.init(groupSalService);
+ this.groupProvider.start(session);
+ /* Meter */
+ final DataBroker meterSalService = session.getSALService(DataBroker.class);
+ this.meterProvider.init(meterSalService);
+ this.meterProvider.start(session);
+ /* FlowNode Reconciliation */
+ final DataBroker dbs = session.getSALService(DataBroker.class);
+ this.flowNodeReconcilProvider.init(dbs);
+ this.flowNodeReconcilProvider.start(session);
+
+ LOG.info("FRMActivator started successfully");
+ } catch (Exception e) {
+ String errMsg = "Unexpected error by starting FRMActivator";
+ LOG.error(errMsg, e);
+ throw new IllegalStateException(errMsg, e);
+ }
}
@Override
protected void stopImpl(final BundleContext context) {
try {
- FRMActivator.flowProvider.close();
- FRMActivator.groupProvider.close();
- FRMActivator.meterProvider.close();
- } catch (Throwable e) {
+ this.flowProvider.close();
+ this.groupProvider.close();
+ this.meterProvider.close();
+ this.flowNodeReconcilProvider.close();
+ } catch (Exception e) {
LOG.error("Unexpected error by stopping FRMActivator", e);
- throw new RuntimeException(e);
}
}
}
\ No newline at end of file
--- /dev/null
+/**
+ * Copyright (c) 2014 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.frm;
+
+import java.math.BigInteger;
+
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.AtomicLongMap;
+
+/**
+ * forwardingrules-manager
+ * org.opendaylight.controller.frm
+ *
+ * Singleton FlowCookieProducer contains a FlowCookie generator which is generated unique
+ * flowCookie identifier for every flow in same Table. That could help with quick
+ * identification of flow statistic because DataStore/CONFIGURATION could contains
+ * a lot of flows with same flowCookie. So we are replacing original flowCookie
+ * with unique and we are building final FlowCookieMap in DataStore/OPERATIONAL
+ *
+ * @author <a href="mailto:vdemcak@cisco.com">Vaclav Demcak</a>
+ *
+ * Created: Jun 13, 2014
+ */
+public enum FlowCookieProducer {
+
+ INSTANCE;
+
+ /* Flow_Cookie_Key and Flow_Ids MapHolder */
+ private static final AtomicLongMap<InstanceIdentifier<Table>> cookieKeys = AtomicLongMap.create();
+
+ /**
+ * Method returns the unique cookie for a node table.
+ * Flow Cookie Key signs List<FlowId> for a right flow statistic identification
+ * in the DataStore/operational.
+ * We need a List<FlowId> because system doesn't guarantee unique mapping
+ * from flow_cookie to flow_id. REST Operations doesn't used FRM yet, so
+ * cookie from user input could have a user input flow ID and an alien system ID
+ * which is generated by system.
+ *
+ * @param InstanceIdentifier<Table> tableIdentifier
+ * @return unique BigInteger flowCookie for a node table
+ */
+ public BigInteger getNewCookie(final InstanceIdentifier<Table> tableIdentifier) {
+ FlowCookieProducer.validationTableIdentifier(tableIdentifier);
+ if ( cookieKeys.containsKey(tableIdentifier)) {
+ /* new identifier always starts from ONE because
+ * ZERO is reserved for the NO_COOKIES flows */
+ return BigInteger.valueOf(cookieKeys.addAndGet(tableIdentifier, 1L));
+ } else {
+ return BigInteger.valueOf(cookieKeys.incrementAndGet(tableIdentifier));
+ }
+ }
+
+ /**
+ * Method cleans the node table flow_cookie_key for the disconnected Node.
+ *
+ * @param InstanceIdentifier<Table> tableIdentifier
+ */
+ public void clean(final InstanceIdentifier<Table> tableIdentifier) {
+ FlowCookieProducer.validationTableIdentifier(tableIdentifier);
+ cookieKeys.remove(tableIdentifier);
+ }
+
+ /*
+ * Help the TableIdentifer input validation method
+ */
+ private static void validationTableIdentifier(final InstanceIdentifier<Table> tableIdent) {
+ Preconditions.checkArgument(tableIdent != null, "Input validation exception: TableIdentifier can not be null !");
+ }
+}
*/
package org.opendaylight.controller.frm.flow;
+import java.math.BigInteger;
+
import org.opendaylight.controller.frm.AbstractChangeListener;
+import org.opendaylight.controller.frm.FlowCookieProducer;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInputBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowTableRef;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.RemoveFlowInputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.UpdateFlowInputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.flow.update.OriginalFlow;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.flow.update.OriginalFlowBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.flow.update.UpdatedFlow;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.flow.update.UpdatedFlowBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowCookie;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowRef;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.base.Preconditions;
+
/**
+ * Flow Change Listener
+ * add, update and remove {@link Flow} processing from {@link org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent}.
*
* @author <a href="mailto:vdemcak@cisco.com">Vaclav Demcak</a>
*
*/
public class FlowChangeListener extends AbstractChangeListener {
- private final static Logger LOG = LoggerFactory.getLogger(FlowChangeListener.class);
+ private static final Logger LOG = LoggerFactory.getLogger(FlowChangeListener.class);
- private final SalFlowService salFlowService;
-
- public SalFlowService getSalFlowService() {
- return this.salFlowService;
- }
+ private final FlowProvider provider;
- public FlowChangeListener(final SalFlowService manager) {
- this.salFlowService = manager;
+ public FlowChangeListener (final FlowProvider provider) {
+ this.provider = Preconditions.checkNotNull(provider, "FlowProvider can not be null !");
}
@Override
- protected void validate() throws IllegalStateException {
- FlowTransactionValidator.validate(this);
- }
+ protected void remove(final InstanceIdentifier<? extends DataObject> identifier,
+ final DataObject removeDataObj) {
- @Override
- protected void remove(InstanceIdentifier<? extends DataObject> identifier, DataObject removeDataObj) {
- if ((removeDataObj instanceof Flow)) {
-
- final Flow flow = ((Flow) removeDataObj);
- final InstanceIdentifier<Table> tableInstanceId = identifier.<Table> firstIdentifierOf(Table.class);
- final InstanceIdentifier<Node> nodeInstanceId = identifier.<Node> firstIdentifierOf(Node.class);
- final RemoveFlowInputBuilder builder = new RemoveFlowInputBuilder(flow);
-
- builder.setFlowRef(new FlowRef(identifier));
- builder.setNode(new NodeRef(nodeInstanceId));
- builder.setFlowTable(new FlowTableRef(tableInstanceId));
-
- Uri uri = new Uri(this.getTransactionId());
- builder.setTransactionUri(uri);
- this.salFlowService.removeFlow((RemoveFlowInput) builder.build());
- LOG.debug("Transaction {} - Removed Flow has removed flow: {}", new Object[]{uri, removeDataObj});
- }
+ final Flow flow = ((Flow) removeDataObj);
+ final InstanceIdentifier<Table> tableIdent = identifier.firstIdentifierOf(Table.class);
+ final InstanceIdentifier<Node> nodeIdent = identifier.firstIdentifierOf(Node.class);
+ final RemoveFlowInputBuilder builder = new RemoveFlowInputBuilder(flow);
+
+ // use empty cookie mask in order to delete flow even with generated cookie
+ builder.setCookieMask(new FlowCookie(BigInteger.ZERO));
+
+ builder.setFlowRef(new FlowRef(identifier));
+ builder.setNode(new NodeRef(nodeIdent));
+ builder.setFlowTable(new FlowTableRef(tableIdent));
+
+ Uri uri = new Uri(this.getTransactionId());
+ builder.setTransactionUri(uri);
+ this.provider.getSalFlowService().removeFlow(builder.build());
+ LOG.debug("Transaction {} - Removed Flow has removed flow: {}", new Object[]{uri, removeDataObj});
}
@Override
- protected void update(InstanceIdentifier<? extends DataObject> identifier, DataObject original, DataObject update) {
- if (original instanceof Flow && update instanceof Flow) {
+ protected void update(final InstanceIdentifier<? extends DataObject> identifier,
+ final DataObject original, final DataObject update) {
+
+ final Flow originalFlow = ((Flow) original);
+ final Flow updatedFlow = ((Flow) update);
+ final InstanceIdentifier<Node> nodeIdent = identifier.firstIdentifierOf(Node.class);
+ final UpdateFlowInputBuilder builder = new UpdateFlowInputBuilder();
- final Flow originalFlow = ((Flow) original);
- final Flow updatedFlow = ((Flow) update);
- final InstanceIdentifier<Node> nodeInstanceId = identifier.<Node>firstIdentifierOf(Node.class);
- final UpdateFlowInputBuilder builder = new UpdateFlowInputBuilder();
+ builder.setNode(new NodeRef(nodeIdent));
+ builder.setFlowRef(new FlowRef(identifier));
- builder.setNode(new NodeRef(nodeInstanceId));
- builder.setFlowRef(new FlowRef(identifier));
+ Uri uri = new Uri(this.getTransactionId());
+ builder.setTransactionUri(uri);
- Uri uri = new Uri(this.getTransactionId());
- builder.setTransactionUri(uri);
+ builder.setUpdatedFlow((new UpdatedFlowBuilder(updatedFlow)).build());
+ builder.setOriginalFlow((new OriginalFlowBuilder(originalFlow)).build());
- builder.setUpdatedFlow((UpdatedFlow) (new UpdatedFlowBuilder(updatedFlow)).build());
- builder.setOriginalFlow((OriginalFlow) (new OriginalFlowBuilder(originalFlow)).build());
+ this.provider.getSalFlowService().updateFlow(builder.build());
+ LOG.debug("Transaction {} - Update Flow has updated flow {} with {}", new Object[]{uri, original, update});
+ }
- this.salFlowService.updateFlow((UpdateFlowInput) builder.build());
- LOG.debug("Transaction {} - Update Flow has updated flow {} with {}", new Object[]{uri, original, update});
- }
+ @Override
+ protected void add(final InstanceIdentifier<? extends DataObject> identifier,
+ final DataObject addDataObj) {
+
+ final Flow flow = ((Flow) addDataObj);
+ final InstanceIdentifier<Table> tableIdent = identifier.firstIdentifierOf(Table.class);
+ final NodeRef nodeRef = new NodeRef(identifier.firstIdentifierOf(Node.class));
+ final FlowCookie flowCookie = new FlowCookie(FlowCookieProducer.INSTANCE.getNewCookie(tableIdent));
+ final AddFlowInputBuilder builder = new AddFlowInputBuilder(flow);
+
+ builder.setNode(nodeRef);
+ builder.setFlowRef(new FlowRef(identifier));
+ builder.setFlowTable(new FlowTableRef(tableIdent));
+ builder.setCookie( flowCookie );
+
+ Uri uri = new Uri(this.getTransactionId());
+ builder.setTransactionUri(uri);
+ this.provider.getSalFlowService().addFlow(builder.build());
+ LOG.debug("Transaction {} - Add Flow has added flow: {}", new Object[]{uri, addDataObj});
}
@Override
- protected void add(InstanceIdentifier<? extends DataObject> identifier, DataObject addDataObj) {
- if ((addDataObj instanceof Flow)) {
-
- final Flow flow = ((Flow) addDataObj);
- final InstanceIdentifier<Table> tableInstanceId = identifier.<Table> firstIdentifierOf(Table.class);
- final InstanceIdentifier<Node> nodeInstanceId = identifier.<Node> firstIdentifierOf(Node.class);
- final AddFlowInputBuilder builder = new AddFlowInputBuilder(flow);
-
- builder.setNode(new NodeRef(nodeInstanceId));
- builder.setFlowRef(new FlowRef(identifier));
- builder.setFlowTable(new FlowTableRef(tableInstanceId));
-
- Uri uri = new Uri(this.getTransactionId());
- builder.setTransactionUri(uri);
- this.salFlowService.addFlow((AddFlowInput) builder.build());
- LOG.debug("Transaction {} - Add Flow has added flow: {}", new Object[]{uri, addDataObj});
- }
+ protected boolean preconditionForChange(final InstanceIdentifier<? extends DataObject> identifier,
+ final DataObject dataObj, final DataObject update) {
+
+ final ReadOnlyTransaction trans = this.provider.getDataService().newReadOnlyTransaction();
+ return update != null
+ ? (dataObj instanceof Flow && update instanceof Flow && isNodeAvailable(identifier, trans))
+ : (dataObj instanceof Flow && isNodeAvailable(identifier, trans));
}
}
*/
package org.opendaylight.controller.frm.flow;
-import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
-import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
-import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.sal.binding.api.RpcConsumerRegistry;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
-import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.base.Preconditions;
+
+/**
+ * Flow Provider registers the {@link FlowChangeListener} and it holds all needed
+ * services for {@link FlowChangeListener}.
+ *
+ * @author <a href="mailto:vdemcak@cisco.com">Vaclav Demcak</a>
+ *
+ */
public class FlowProvider implements AutoCloseable {
- private final static Logger LOG = LoggerFactory.getLogger(FlowProvider.class);
+ private static final Logger LOG = LoggerFactory.getLogger(FlowProvider.class);
private SalFlowService salFlowService;
- private DataProviderService dataService;
+ private DataBroker dataService;
/* DataChangeListener */
- private FlowChangeListener flowDataChangeListener;
- ListenerRegistration<DataChangeListener> flowDataChangeListenerRegistration;
+ private DataChangeListener flowDataChangeListener;
+ private ListenerRegistration<DataChangeListener> flowDataChangeListenerRegistration;
+
+ /**
+ * Provider Initialization Phase.
+ *
+ * @param DataProviderService dataService
+ */
+ public void init (final DataBroker dataService) {
+ LOG.info("FRM Flow Config Provider initialization.");
+ this.dataService = Preconditions.checkNotNull(dataService, "DataProviderService can not be null !");
+ }
+
+ /**
+ * Listener Registration Phase
+ *
+ * @param RpcConsumerRegistry rpcRegistry
+ */
+ public void start(final RpcConsumerRegistry rpcRegistry) {
+ Preconditions.checkArgument(rpcRegistry != null, "RpcConsumerRegistry can not be null !");
+
+ this.salFlowService = Preconditions.checkNotNull(rpcRegistry.getRpcService(SalFlowService.class),
+ "RPC SalFlowService not found.");
- public void start() {
/* Build Path */
- InstanceIdentifierBuilder<Nodes> nodesBuilder = InstanceIdentifier.<Nodes> builder(Nodes.class);
- InstanceIdentifierBuilder<Node> nodeChild = nodesBuilder.<Node> child(Node.class);
- InstanceIdentifierBuilder<FlowCapableNode> augmentFlowCapNode = nodeChild.<FlowCapableNode> augmentation(FlowCapableNode.class);
- InstanceIdentifierBuilder<Table> tableChild = augmentFlowCapNode.<Table> child(Table.class);
- InstanceIdentifierBuilder<Flow> flowChild = tableChild.<Flow> child(Flow.class);
- final InstanceIdentifier<? extends DataObject> flowDataObjectPath = flowChild.toInstance();
+ InstanceIdentifier<Flow> flowIdentifier = InstanceIdentifier.create(Nodes.class)
+ .child(Node.class).augmentation(FlowCapableNode.class).child(Table.class).child(Flow.class);
/* DataChangeListener registration */
- this.flowDataChangeListener = new FlowChangeListener(this.salFlowService);
- this.flowDataChangeListenerRegistration = this.dataService.registerDataChangeListener(flowDataObjectPath, flowDataChangeListener);
- LOG.info("Flow Config Provider started.");
- }
+ this.flowDataChangeListener = new FlowChangeListener(FlowProvider.this);
+ this.flowDataChangeListenerRegistration =
+ this.dataService.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
+ flowIdentifier, flowDataChangeListener, DataChangeScope.SUBTREE);
- protected DataModificationTransaction startChange() {
- return this.dataService.beginTransaction();
+ LOG.info("FRM Flow Config Provider started.");
}
@Override
- public void close() throws Exception {
- if(flowDataChangeListenerRegistration != null){
- flowDataChangeListenerRegistration.close();
+ public void close() {
+ LOG.info("FRM Flow Config Provider stopped.");
+ if (flowDataChangeListenerRegistration != null) {
+ try {
+ flowDataChangeListenerRegistration.close();
+ } catch (Exception e) {
+ String errMsg = "Error by stop FRM Flow Config Provider.";
+ LOG.error(errMsg, e);
+ throw new IllegalStateException(errMsg, e);
+ } finally {
+ flowDataChangeListenerRegistration = null;
+ }
}
}
- public void setDataService(final DataProviderService dataService) {
- this.dataService = dataService;
+ public DataChangeListener getFlowDataChangeListener() {
+ return flowDataChangeListener;
+ }
+
+ public SalFlowService getSalFlowService() {
+ return salFlowService;
}
- public void setSalFlowService(final SalFlowService salFlowService) {
- this.salFlowService = salFlowService;
+ public DataBroker getDataService() {
+ return dataService;
}
}
+++ /dev/null
-/**
- * Copyright (c) 2014 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.frm.flow;
-
-public class FlowTransactionValidator {
-
- public static void validate(FlowChangeListener transaction) throws IllegalStateException {
- // NOOP
- }
-}
package org.opendaylight.controller.frm.group;
import org.opendaylight.controller.frm.AbstractChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupInputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.RemoveGroupInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.RemoveGroupInputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.SalGroupService;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.UpdateGroupInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.UpdateGroupInputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.group.update.OriginalGroup;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.group.update.OriginalGroupBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.group.update.UpdatedGroup;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.group.update.UpdatedGroupBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupRef;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.base.Preconditions;
+
/**
+ * Group Change Listener
+ * add, update and remove {@link Group} processing from {@link org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent}.
*
* @author <a href="mailto:vdemcak@cisco.com">Vaclav Demcak</a>
*
*/
public class GroupChangeListener extends AbstractChangeListener {
- private final static Logger LOG = LoggerFactory.getLogger(GroupChangeListener.class);
-
- private final SalGroupService salGroupService;
+ private static final Logger LOG = LoggerFactory.getLogger(GroupChangeListener.class);
- public SalGroupService getSalGroupService() {
- return this.salGroupService;
- }
+ private final GroupProvider provider;
- public GroupChangeListener(final SalGroupService manager) {
- this.salGroupService = manager;
+ public GroupChangeListener(final GroupProvider provider) {
+ this.provider = Preconditions.checkNotNull(provider, "GroupProvider can not be null !");
}
@Override
- protected void validate() throws IllegalStateException {
- GroupTransactionValidator.validate(this);
+ protected void remove(final InstanceIdentifier<? extends DataObject> identifier,
+ final DataObject removeDataObj) {
+
+ final Group group = ((Group) removeDataObj);
+ final InstanceIdentifier<Node> nodeInstanceId = identifier.<Node> firstIdentifierOf(Node.class);
+ final RemoveGroupInputBuilder builder = new RemoveGroupInputBuilder(group);
+
+ builder.setNode(new NodeRef(nodeInstanceId));
+ builder.setGroupRef(new GroupRef(identifier));
+
+ Uri uri = new Uri(this.getTransactionId());
+ builder.setTransactionUri(uri);
+ this.provider.getSalGroupService().removeGroup(builder.build());
+ LOG.debug("Transaction {} - Remove Group has removed group: {}", new Object[]{uri, removeDataObj});
}
@Override
- protected void remove(InstanceIdentifier<? extends DataObject> identifier, DataObject removeDataObj) {
- if ((removeDataObj instanceof Group)) {
+ protected void update(final InstanceIdentifier<? extends DataObject> identifier,
+ final DataObject original, final DataObject update) {
- final Group group = ((Group) removeDataObj);
- final InstanceIdentifier<Node> nodeInstanceId = identifier.<Node> firstIdentifierOf(Node.class);
- final RemoveGroupInputBuilder builder = new RemoveGroupInputBuilder(group);
+ final Group originalGroup = ((Group) original);
+ final Group updatedGroup = ((Group) update);
+ final InstanceIdentifier<Node> nodeInstanceId = identifier.<Node> firstIdentifierOf(Node.class);
+ final UpdateGroupInputBuilder builder = new UpdateGroupInputBuilder();
- builder.setNode(new NodeRef(nodeInstanceId));
- builder.setGroupRef(new GroupRef(identifier));
+ builder.setNode(new NodeRef(nodeInstanceId));
+ builder.setGroupRef(new GroupRef(identifier));
- Uri uri = new Uri(this.getTransactionId());
- builder.setTransactionUri(uri);
- this.salGroupService.removeGroup((RemoveGroupInput) builder.build());
- LOG.debug("Transaction {} - Remove Group has removed group: {}", new Object[]{uri, removeDataObj});
- }
- }
+ Uri uri = new Uri(this.getTransactionId());
+ builder.setTransactionUri(uri);
- @Override
- protected void update(InstanceIdentifier<? extends DataObject> identifier, DataObject original, DataObject update) {
- if (original instanceof Group && update instanceof Group) {
+ builder.setUpdatedGroup((new UpdatedGroupBuilder(updatedGroup)).build());
+ builder.setOriginalGroup((new OriginalGroupBuilder(originalGroup)).build());
- final Group originalGroup = ((Group) original);
- final Group updatedGroup = ((Group) update);
- final InstanceIdentifier<Node> nodeInstanceId = identifier.<Node> firstIdentifierOf(Node.class);
- final UpdateGroupInputBuilder builder = new UpdateGroupInputBuilder();
+ this.provider.getSalGroupService().updateGroup(builder.build());
+ LOG.debug("Transaction {} - Update Group has updated group {} with group {}", new Object[]{uri, original, update});
+ }
- builder.setNode(new NodeRef(nodeInstanceId));
- builder.setGroupRef(new GroupRef(identifier));
+ @Override
+ protected void add(final InstanceIdentifier<? extends DataObject> identifier,
+ final DataObject addDataObj) {
- Uri uri = new Uri(this.getTransactionId());
- builder.setTransactionUri(uri);
+ final Group group = ((Group) addDataObj);
+ final InstanceIdentifier<Node> nodeInstanceId = identifier.<Node> firstIdentifierOf(Node.class);
+ final AddGroupInputBuilder builder = new AddGroupInputBuilder(group);
- builder.setUpdatedGroup((UpdatedGroup) (new UpdatedGroupBuilder(updatedGroup)).build());
- builder.setOriginalGroup((OriginalGroup) (new OriginalGroupBuilder(originalGroup)).build());
+ builder.setNode(new NodeRef(nodeInstanceId));
+ builder.setGroupRef(new GroupRef(identifier));
- this.salGroupService.updateGroup((UpdateGroupInput) builder.build());
- LOG.debug("Transaction {} - Update Group has updated group {} with group {}", new Object[]{uri, original, update});
- }
+ Uri uri = new Uri(this.getTransactionId());
+ builder.setTransactionUri(uri);
+ this.provider.getSalGroupService().addGroup(builder.build());
+ LOG.debug("Transaction {} - Add Group has added group: {}", new Object[]{uri, addDataObj});
}
@Override
- protected void add(InstanceIdentifier<? extends DataObject> identifier, DataObject addDataObj) {
- if ((addDataObj instanceof Group)) {
- final Group group = ((Group) addDataObj);
- final InstanceIdentifier<Node> nodeInstanceId = identifier.<Node> firstIdentifierOf(Node.class);
- final AddGroupInputBuilder builder = new AddGroupInputBuilder(group);
-
- builder.setNode(new NodeRef(nodeInstanceId));
- builder.setGroupRef(new GroupRef(identifier));
-
- Uri uri = new Uri(this.getTransactionId());
- builder.setTransactionUri(uri);
- this.salGroupService.addGroup((AddGroupInput) builder.build());
- LOG.debug("Transaction {} - Add Group has added group: {}", new Object[]{uri, addDataObj});
- }
+ protected boolean preconditionForChange(final InstanceIdentifier<? extends DataObject> identifier,
+ final DataObject dataObj, final DataObject update) {
+
+ final ReadOnlyTransaction trans = this.provider.getDataService().newReadOnlyTransaction();
+ return update != null
+ ? (dataObj instanceof Group && update instanceof Group && isNodeAvailable(identifier, trans))
+ : (dataObj instanceof Group && isNodeAvailable(identifier, trans));
}
}
*/
package org.opendaylight.controller.frm.group;
-import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
-import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
-import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.sal.binding.api.RpcConsumerRegistry;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.SalGroupService;
import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
-import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.base.Preconditions;
+
+/**
+ * Group Provider registers the {@link GroupChangeListener} and it holds all needed
+ * services for {@link GroupChangeListener}.
+ *
+ * @author <a href="mailto:vdemcak@cisco.com">Vaclav Demcak</a>
+ *
+ */
public class GroupProvider implements AutoCloseable {
- private final static Logger LOG = LoggerFactory.getLogger(GroupProvider.class);
+ private static final Logger LOG = LoggerFactory.getLogger(GroupProvider.class);
private SalGroupService salGroupService;
- private DataProviderService dataService;
+ private DataBroker dataService;
/* DataChangeListener */
- private GroupChangeListener groupDataChangeListener;
- ListenerRegistration<DataChangeListener> groupDataChangeListenerRegistration;
+ private DataChangeListener groupDataChangeListener;
+ private ListenerRegistration<DataChangeListener> groupDataChangeListenerRegistration;
+
+ /**
+ * Provider Initialization Phase.
+ *
+ * @param DataProviderService dataService
+ */
+ public void init (final DataBroker dataService) {
+ LOG.info("FRM Group Config Provider initialization.");
+ this.dataService = Preconditions.checkNotNull(dataService, "DataService can not be null !");
+ }
+
+ /**
+ * Listener Registration Phase
+ *
+ * @param RpcConsumerRegistry rpcRegistry
+ */
+ public void start(final RpcConsumerRegistry rpcRegistry) {
+ Preconditions.checkArgument(rpcRegistry != null, "RpcConsumerRegistry can not be null !");
+
+ this.salGroupService = Preconditions.checkNotNull(rpcRegistry.getRpcService(SalGroupService.class),
+ "RPC SalGroupService not found.");
- public void start() {
/* Build Path */
- InstanceIdentifierBuilder<Nodes> nodesBuilder = InstanceIdentifier.<Nodes> builder(Nodes.class);
- InstanceIdentifierBuilder<Node> nodeChild = nodesBuilder.<Node> child(Node.class);
- InstanceIdentifierBuilder<FlowCapableNode> augmentFlowCapNode = nodeChild.<FlowCapableNode> augmentation(FlowCapableNode.class);
- InstanceIdentifierBuilder<Group> groupChild = augmentFlowCapNode.<Group> child(Group.class);
- final InstanceIdentifier<? extends DataObject> groupDataObjectPath = groupChild.toInstance();
+ InstanceIdentifier<Group> groupIdentifier = InstanceIdentifier.create(Nodes.class)
+ .child(Node.class).augmentation(FlowCapableNode.class).child(Group.class);
/* DataChangeListener registration */
- this.groupDataChangeListener = new GroupChangeListener(this.salGroupService);
- this.groupDataChangeListenerRegistration = this.dataService.registerDataChangeListener(groupDataObjectPath, groupDataChangeListener);
- LOG.info("Group Config Provider started.");
- }
+ this.groupDataChangeListener = new GroupChangeListener(GroupProvider.this);
+ this.groupDataChangeListenerRegistration = this.dataService.registerDataChangeListener(
+ LogicalDatastoreType.CONFIGURATION, groupIdentifier, groupDataChangeListener, DataChangeScope.SUBTREE);
- protected DataModificationTransaction startChange() {
- return this.dataService.beginTransaction();
+ LOG.info("FRM Group Config Provider started.");
}
- public void close() throws Exception {
- if(groupDataChangeListenerRegistration != null){
- groupDataChangeListenerRegistration.close();
+ @Override
+ public void close() {
+ LOG.info("FRM Group Config Provider stopped.");
+ if (groupDataChangeListenerRegistration != null) {
+ try {
+ groupDataChangeListenerRegistration.close();
+ } catch (Exception e) {
+ String errMsg = "Error by stop FRM Group Config Provider.";
+ LOG.error(errMsg, e);
+ throw new IllegalStateException(errMsg, e);
+ } finally {
+ groupDataChangeListenerRegistration = null;
+ }
}
}
- public void setDataService(final DataProviderService dataService) {
- this.dataService = dataService;
+ public DataChangeListener getGroupDataChangeListener() {
+ return groupDataChangeListener;
+ }
+
+ public SalGroupService getSalGroupService() {
+ return salGroupService;
}
- public void setSalGroupService(final SalGroupService salGroupService) {
- this.salGroupService = salGroupService;
+ public DataBroker getDataService() {
+ return dataService;
}
}
+++ /dev/null
-/**
- * Copyright (c) 2014 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.frm.group;
-
-public class GroupTransactionValidator {
-
- public static void validate(GroupChangeListener transaction) throws IllegalStateException {
- // NOOP
- }
-}
package org.opendaylight.controller.frm.meter;
import org.opendaylight.controller.frm.AbstractChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.AddMeterInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.AddMeterInputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.RemoveMeterInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.RemoveMeterInputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.SalMeterService;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.UpdateMeterInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.UpdateMeterInputBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.meter.update.OriginalMeter;
import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.meter.update.OriginalMeterBuilder;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.meter.update.UpdatedMeter;
import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.meter.update.UpdatedMeterBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.Meter;
import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.MeterRef;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.base.Preconditions;
+
/**
+ * Meter Change Listener
+ * add, update and remove {@link Meter} processing from {@link org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent}.
*
* @author <a href="mailto:vdemcak@cisco.com">Vaclav Demcak</a>
*
*/
public class MeterChangeListener extends AbstractChangeListener {
- private final static Logger LOG = LoggerFactory.getLogger(MeterChangeListener.class);
-
- private final SalMeterService salMeterService;
-
- public SalMeterService getSalMeterService() {
- return this.salMeterService;
- }
+ private static final Logger LOG = LoggerFactory.getLogger(MeterChangeListener.class);
- public MeterChangeListener(final SalMeterService manager) {
- this.salMeterService = manager;
- }
+ private final MeterProvider provider;
- @Override
- protected void validate() throws IllegalStateException {
- MeterTransactionValidator.validate(this);
+ public MeterChangeListener (final MeterProvider provider) {
+ this.provider = Preconditions.checkNotNull(provider, "MeterProvider can not be null !");
}
@Override
- protected void remove(InstanceIdentifier<? extends DataObject> identifier, DataObject removeDataObj) {
- if ((removeDataObj instanceof Meter)) {
+ protected void remove(final InstanceIdentifier<? extends DataObject> identifier,
+ final DataObject removeDataObj) {
- final Meter meter = ((Meter) removeDataObj);
- final InstanceIdentifier<Node> nodeInstanceId = identifier.<Node> firstIdentifierOf(Node.class);
- final RemoveMeterInputBuilder builder = new RemoveMeterInputBuilder(meter);
+ final Meter meter = ((Meter) removeDataObj);
+ final InstanceIdentifier<Node> nodeIdent = identifier.firstIdentifierOf(Node.class);
+ final RemoveMeterInputBuilder builder = new RemoveMeterInputBuilder(meter);
- builder.setNode(new NodeRef(nodeInstanceId));
- builder.setMeterRef(new MeterRef(identifier));
+ builder.setNode(new NodeRef(nodeIdent));
+ builder.setMeterRef(new MeterRef(identifier));
- Uri uri = new Uri(this.getTransactionId());
- builder.setTransactionUri(uri);
- this.salMeterService.removeMeter((RemoveMeterInput) builder.build());
- LOG.debug("Transaction {} - Remove Meter has removed meter: {}", new Object[]{uri, removeDataObj});
- }
+ Uri uri = new Uri(this.getTransactionId());
+ builder.setTransactionUri(uri);
+ this.provider.getSalMeterService().removeMeter(builder.build());
+ LOG.debug("Transaction {} - Remove Meter has removed meter: {}", new Object[]{uri, removeDataObj});
}
@Override
- protected void update(InstanceIdentifier<? extends DataObject> identifier, DataObject original, DataObject update) {
- if (original instanceof Meter && update instanceof Meter) {
+ protected void update(final InstanceIdentifier<? extends DataObject> identifier,
+ final DataObject original, final DataObject update) {
- final Meter originalMeter = ((Meter) original);
- final Meter updatedMeter = ((Meter) update);
- final InstanceIdentifier<Node> nodeInstanceId = identifier.<Node> firstIdentifierOf(Node.class);
- final UpdateMeterInputBuilder builder = new UpdateMeterInputBuilder();
+ final Meter originalMeter = ((Meter) original);
+ final Meter updatedMeter = ((Meter) update);
+ final InstanceIdentifier<Node> nodeInstanceId = identifier.firstIdentifierOf(Node.class);
+ final UpdateMeterInputBuilder builder = new UpdateMeterInputBuilder();
- builder.setNode(new NodeRef(nodeInstanceId));
- builder.setMeterRef(new MeterRef(identifier));
+ builder.setNode(new NodeRef(nodeInstanceId));
+ builder.setMeterRef(new MeterRef(identifier));
- Uri uri = new Uri(this.getTransactionId());
- builder.setTransactionUri(uri);
+ Uri uri = new Uri(this.getTransactionId());
+ builder.setTransactionUri(uri);
- builder.setUpdatedMeter((UpdatedMeter) (new UpdatedMeterBuilder(updatedMeter)).build());
- builder.setOriginalMeter((OriginalMeter) (new OriginalMeterBuilder(originalMeter)).build());
+ builder.setUpdatedMeter((new UpdatedMeterBuilder(updatedMeter)).build());
+ builder.setOriginalMeter((new OriginalMeterBuilder(originalMeter)).build());
+
+ this.provider.getSalMeterService().updateMeter(builder.build());
+ LOG.debug("Transaction {} - Update Meter has updated meter {} with {}", new Object[]{uri, original, update});
- this.salMeterService.updateMeter((UpdateMeterInput) builder.build());
- LOG.debug("Transaction {} - Update Meter has updated meter {} with {}", new Object[]{uri, original, update});
- }
}
@Override
- protected void add(InstanceIdentifier<? extends DataObject> identifier, DataObject addDataObj) {
- if ((addDataObj instanceof Meter)) {
+ protected void add(final InstanceIdentifier<? extends DataObject> identifier,
+ final DataObject addDataObj) {
+
+ final Meter meter = ((Meter) addDataObj);
+ final InstanceIdentifier<Node> nodeInstanceId = identifier.firstIdentifierOf(Node.class);
+ final AddMeterInputBuilder builder = new AddMeterInputBuilder(meter);
- final Meter meter = ((Meter) addDataObj);
- final InstanceIdentifier<Node> nodeInstanceId = identifier.<Node> firstIdentifierOf(Node.class);
- final AddMeterInputBuilder builder = new AddMeterInputBuilder(meter);
+ builder.setNode(new NodeRef(nodeInstanceId));
+ builder.setMeterRef(new MeterRef(identifier));
- builder.setNode(new NodeRef(nodeInstanceId));
- builder.setMeterRef(new MeterRef(identifier));
+ Uri uri = new Uri(this.getTransactionId());
+ builder.setTransactionUri(uri);
+ this.provider.getSalMeterService().addMeter(builder.build());
+ LOG.debug("Transaction {} - Add Meter has added meter: {}", new Object[]{uri, addDataObj});
+ }
+
+ @Override
+ protected boolean preconditionForChange(final InstanceIdentifier<? extends DataObject> identifier,
+ final DataObject dataObj, final DataObject update) {
- Uri uri = new Uri(this.getTransactionId());
- builder.setTransactionUri(uri);
- this.salMeterService.addMeter((AddMeterInput) builder.build());
- LOG.debug("Transaction {} - Add Meter has added meter: {}", new Object[]{uri, addDataObj});
- }
+ final ReadOnlyTransaction trans = this.provider.getDataService().newReadOnlyTransaction();
+ return update != null
+ ? (dataObj instanceof Meter && update instanceof Meter && isNodeAvailable(identifier, trans))
+ : (dataObj instanceof Meter && isNodeAvailable(identifier, trans));
}
-}
\ No newline at end of file
+}
*/
package org.opendaylight.controller.frm.meter;
-import org.opendaylight.controller.sal.binding.api.data.DataChangeListener;
-import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
-import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.sal.binding.api.RpcConsumerRegistry;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.Meter;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.SalMeterService;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
-import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.base.Preconditions;
+
+/**
+ * Meter Provider registers the {@link MeterChangeListener} and it holds all needed
+ * services for {@link MeterChangeListener}.
+ *
+ * @author <a href="mailto:vdemcak@cisco.com">Vaclav Demcak</a>
+ *
+ */
public class MeterProvider implements AutoCloseable {
- private final static Logger LOG = LoggerFactory.getLogger(MeterProvider.class);
+ private static final Logger LOG = LoggerFactory.getLogger(MeterProvider.class);
- private DataProviderService dataService;
private SalMeterService salMeterService;
+ private DataBroker dataService;
/* DataChangeListener */
- private MeterChangeListener meterDataChangeListener;
- ListenerRegistration<DataChangeListener> meterDataChangeListenerRegistration;
+ private DataChangeListener meterDataChangeListener;
+ private ListenerRegistration<DataChangeListener> meterDataChangeListenerRegistration;
+
+ /**
+ * Provider Initialization Phase.
+ *
+ * @param DataProviderService dataService
+ */
+ public void init(final DataBroker dataService) {
+ LOG.info("FRM Meter Config Provider initialization.");
+ this.dataService = Preconditions.checkNotNull(dataService, "DataProviderService can not be null !");
+ }
+
+ /**
+ * Listener Registration Phase
+ *
+ * @param RpcConsumerRegistry rpcRegistry
+ */
+ public void start(final RpcConsumerRegistry rpcRegistry) {
+ Preconditions.checkArgument(rpcRegistry != null, "RpcConsumerRegistry can not be null !");
+ this.salMeterService = Preconditions.checkNotNull(rpcRegistry.getRpcService(SalMeterService.class),
+ "RPC SalMeterService not found.");
- public void start() {
/* Build Path */
- InstanceIdentifierBuilder<Nodes> nodesBuilder = InstanceIdentifier.<Nodes> builder(Nodes.class);
- InstanceIdentifierBuilder<Node> nodeChild = nodesBuilder.<Node> child(Node.class);
- InstanceIdentifierBuilder<FlowCapableNode> augmentFlowCapNode = nodeChild.<FlowCapableNode> augmentation(FlowCapableNode.class);
- InstanceIdentifierBuilder<Meter> meterChild = augmentFlowCapNode.<Meter> child(Meter.class);
- final InstanceIdentifier<? extends DataObject> meterDataObjectPath = meterChild.toInstance();
+ InstanceIdentifier<Meter> meterIdentifier = InstanceIdentifier.create(Nodes.class)
+ .child(Node.class).augmentation(FlowCapableNode.class).child(Meter.class);
/* DataChangeListener registration */
- this.meterDataChangeListener = new MeterChangeListener(this.salMeterService);
- this.meterDataChangeListenerRegistration = this.dataService.registerDataChangeListener(meterDataObjectPath, meterDataChangeListener);
- LOG.info("Meter Config Provider started.");
- }
+ this.meterDataChangeListener = new MeterChangeListener(MeterProvider.this);
+ this.meterDataChangeListenerRegistration =
+ this.dataService.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
+ meterIdentifier, meterDataChangeListener, DataChangeScope.SUBTREE);
- protected DataModificationTransaction startChange() {
- return this.dataService.beginTransaction();
+ LOG.info("FRM Meter Config Provider started.");
}
- public void close() throws Exception {
- if(meterDataChangeListenerRegistration != null){
- meterDataChangeListenerRegistration.close();
+ @Override
+ public void close() {
+ LOG.info("FRM Meter Config Provider stopped.");
+ if (meterDataChangeListenerRegistration != null) {
+ try {
+ meterDataChangeListenerRegistration.close();
+ } catch (Exception e) {
+ String errMsg = "Error by stop FRM Meter Config Provider.";
+ LOG.error(errMsg, e);
+ throw new IllegalStateException(errMsg, e);
+ } finally {
+ meterDataChangeListenerRegistration = null;
+ }
}
}
- public void setDataService(final DataProviderService dataService) {
- this.dataService = dataService;
+ public DataChangeListener getMeterDataChangeListener() {
+ return meterDataChangeListener;
+ }
+
+ public DataBroker getDataService() {
+ return dataService;
}
- public void setSalMeterService(final SalMeterService salMeterService) {
- this.salMeterService = salMeterService;
+ public SalMeterService getSalMeterService() {
+ return salMeterService;
}
-}
\ No newline at end of file
+}
+++ /dev/null
-/**
- * Copyright (c) 2014 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.frm.meter;
-
-public class MeterTransactionValidator {
-
- public static void validate(MeterChangeListener transaction) throws IllegalStateException {
- // NOOP
- }
-}
--- /dev/null
+/**
+ * Copyright (c) 2014 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.frm.reconil;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+
+import org.opendaylight.controller.frm.AbstractChangeListener;
+import org.opendaylight.controller.frm.FlowCookieProducer;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.meters.Meter;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowTableRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowCookie;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.AddGroupInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.GroupRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.types.rev131018.groups.Group;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.AddMeterInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.MeterRef;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/**
+ * forwardingrules-manager
+ * org.opendaylight.controller.frm
+ *
+ * FlowNode Reconciliation Listener
+ * Reconciliation for a new FlowNode
+ * Remove CookieMapKey for removed FlowNode
+ *
+ * @author <a href="mailto:vdemcak@cisco.com">Vaclav Demcak</a>
+ *
+ * Created: Jun 13, 2014
+ */
+public class FlowNodeReconcilListener extends AbstractChangeListener {
+
+ private static final Logger LOG = LoggerFactory.getLogger(FlowNodeReconcilListener.class);
+
+ private final FlowNodeReconcilProvider provider;
+
+ public FlowNodeReconcilListener(final FlowNodeReconcilProvider provider) {
+ this.provider = Preconditions.checkNotNull(provider, "Flow Node Reconcil Provider can not be null!");
+ }
+
+ @Override
+ public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> changeEvent) {
+ /* FlowCapableNode DataObjects for reconciliation */
+ final Set<Entry<InstanceIdentifier<? extends DataObject>, DataObject>> createdEntries =
+ changeEvent.getCreatedData().entrySet();
+ /* FlowCapableNode DataObjects for clean FlowCookieHolder */
+ final Set<InstanceIdentifier<? extends DataObject>> removeEntriesInstanceIdentifiers =
+ changeEvent.getRemovedPaths();
+ for (final Entry<InstanceIdentifier<? extends DataObject>, DataObject> createdEntry : createdEntries) {
+ InstanceIdentifier<? extends DataObject> entryKey = createdEntry.getKey();
+ DataObject entryValue = createdEntry.getValue();
+ if (preconditionForChange(entryKey, entryValue, null)) {
+ this.add(entryKey, entryValue);
+ }
+ }
+ for (final InstanceIdentifier<?> instanceId : removeEntriesInstanceIdentifiers) {
+ Map<InstanceIdentifier<? extends DataObject>, DataObject> origConfigData =
+ changeEvent.getOriginalData();
+ final DataObject removeValue = origConfigData.get(instanceId);
+ if (preconditionForChange(instanceId, removeValue, null)) {
+ this.remove(instanceId, removeValue);
+ }
+ }
+ }
+
+ @Override
+ /* Cleaning FlowCookieManager holder for all node tables */
+ protected void remove(final InstanceIdentifier<? extends DataObject> identifier,
+ final DataObject removeDataObj) {
+
+ final InstanceIdentifier<FlowCapableNode> flowNodeIdent =
+ identifier.firstIdentifierOf(FlowCapableNode.class);
+ final FlowCapableNode flowNode = ((FlowCapableNode) removeDataObj);
+
+ for (Table flowTable : flowNode.getTable()) {
+ final InstanceIdentifier<Table> tableIdent =
+ flowNodeIdent.child(Table.class, flowTable.getKey());
+ FlowCookieProducer.INSTANCE.clean(tableIdent);
+ }
+ }
+
+ @Override
+ /* Reconciliation by connect new FlowCapableNode */
+ protected void add(final InstanceIdentifier<? extends DataObject> identifier,
+ final DataObject addDataObj) {
+
+ final InstanceIdentifier<FlowCapableNode> flowNodeIdent =
+ identifier.firstIdentifierOf(FlowCapableNode.class);
+ final Optional<FlowCapableNode> flowCapNode = this.readFlowCapableNode(flowNodeIdent);
+
+ if (flowCapNode.isPresent()) {
+ final InstanceIdentifier<Node> nodeIdent = identifier.firstIdentifierOf(Node.class);
+ final NodeRef nodeRef = new NodeRef(nodeIdent);
+ /* Groups - have to be first */
+ for (Group group : flowCapNode.get().getGroup()) {
+ final GroupRef groupRef = new GroupRef(flowNodeIdent.child(Group.class, group.getKey()));
+ final AddGroupInputBuilder groupBuilder = new AddGroupInputBuilder(group);
+ groupBuilder.setGroupRef(groupRef);
+ groupBuilder.setNode(nodeRef);
+ this.provider.getSalGroupService().addGroup(groupBuilder.build());
+ }
+ /* Meters */
+ for (Meter meter : flowCapNode.get().getMeter()) {
+ final MeterRef meterRef = new MeterRef(flowNodeIdent.child(Meter.class, meter.getKey()));
+ final AddMeterInputBuilder meterBuilder = new AddMeterInputBuilder(meter);
+ meterBuilder.setMeterRef(meterRef);
+ meterBuilder.setNode(nodeRef);
+ this.provider.getSalMeterService().addMeter(meterBuilder.build());
+ }
+ /* Flows */
+ for (Table flowTable : flowCapNode.get().getTable()) {
+ final InstanceIdentifier<Table> tableIdent = flowNodeIdent.child(Table.class, flowTable.getKey());
+ for (Flow flow : flowTable.getFlow()) {
+ final FlowCookie flowCookie = new FlowCookie(FlowCookieProducer.INSTANCE.getNewCookie(tableIdent));
+ final FlowRef flowRef = new FlowRef(tableIdent.child(Flow.class, flow.getKey()));
+ final FlowTableRef flowTableRef = new FlowTableRef(tableIdent);
+ final AddFlowInputBuilder flowBuilder = new AddFlowInputBuilder(flow);
+ flowBuilder.setCookie(flowCookie);
+ flowBuilder.setNode(nodeRef);
+ flowBuilder.setFlowTable(flowTableRef);
+ flowBuilder.setFlowRef(flowRef);
+ this.provider.getSalFlowService().addFlow(flowBuilder.build());
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void update(final InstanceIdentifier<? extends DataObject> identifier,
+ final DataObject original, DataObject update) {
+ // NOOP - Listener is registered for DataChangeScope.BASE only
+ }
+
+ @Override
+ protected boolean preconditionForChange(final InstanceIdentifier<? extends DataObject> identifier,
+ final DataObject dataObj, final DataObject update) {
+ return (dataObj instanceof FlowCapableNode);
+ }
+
+ private Optional<FlowCapableNode> readFlowCapableNode(final InstanceIdentifier<FlowCapableNode> flowNodeIdent) {
+ ReadOnlyTransaction readTrans = this.provider.getDataService().newReadOnlyTransaction();
+ try {
+ ListenableFuture<Optional<FlowCapableNode>> confFlowNode =
+ readTrans.read(LogicalDatastoreType.CONFIGURATION, flowNodeIdent);
+ if (confFlowNode.get().isPresent()) {
+ return Optional.<FlowCapableNode> of(confFlowNode.get().get());
+ } else {
+ return Optional.absent();
+ }
+ }
+ catch (InterruptedException | ExecutionException e) {
+ LOG.error("Unexpected exception by reading flow ".concat(flowNodeIdent.toString()), e);
+ return Optional.absent();
+ }
+ finally {
+ readTrans.close();
+ }
+ }
+}
--- /dev/null
+/**
+ * Copyright (c) 2014 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.frm.reconil;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.sal.binding.api.RpcConsumerRegistry;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.group.service.rev130918.SalGroupService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.SalMeterService;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Preconditions;
+
+/**
+ * forwardingrules-manager
+ * org.opendaylight.controller.frm
+ *
+ * FlowNode Reconciliation Provider registers the FlowNodeReconilListener
+ * and it holds all needed services for FlowNodeReconcilListener.
+ *
+ * @author <a href="mailto:vdemcak@cisco.com">Vaclav Demcak</a>
+ *
+ * Created: Jun 13, 2014
+ */
+public class FlowNodeReconcilProvider implements AutoCloseable {
+
+ private static final Logger LOG = LoggerFactory.getLogger(FlowNodeReconcilProvider.class);
+
+ private SalFlowService salFlowService;
+ private SalMeterService salMeterService;
+ private SalGroupService salGroupService;
+ private DataBroker dataService;
+
+ /* DataChangeListener */
+ private DataChangeListener flowNodeReconcilListener;
+ private ListenerRegistration<DataChangeListener> flowNodeReconcilListenerRegistration;
+
+ public void init (final DataBroker dataService) {
+ LOG.info("FRM Flow Node Config Reconcil Provider initialization.");
+
+ this.dataService = Preconditions.checkNotNull(dataService, "DataProviderService can not be null !");
+ }
+
+ public void start( final RpcConsumerRegistry rpcRegistry ) {
+ Preconditions.checkArgument(rpcRegistry != null, "RpcConcumerRegistry can not be null !");
+
+ this.salFlowService = Preconditions.checkNotNull(rpcRegistry.getRpcService(SalFlowService.class),
+ "RPC SalFlowService not found.");
+ this.salMeterService = Preconditions.checkNotNull(rpcRegistry.getRpcService(SalMeterService.class),
+ "RPC SalMeterService not found.");
+ this.salGroupService = Preconditions.checkNotNull(rpcRegistry.getRpcService(SalGroupService.class),
+ "RPC SalGroupService not found.");
+
+ /* Build Path */
+ InstanceIdentifier<FlowCapableNode> flowCapableNodeIdent =
+ InstanceIdentifier.create(Nodes.class).child(Node.class).augmentation(FlowCapableNode.class);
+
+ /* ReconcilNotificationListener registration */
+ this.flowNodeReconcilListener = new FlowNodeReconcilListener(FlowNodeReconcilProvider.this);
+ this.flowNodeReconcilListenerRegistration = this.dataService.registerDataChangeListener(
+ LogicalDatastoreType.OPERATIONAL, flowCapableNodeIdent, flowNodeReconcilListener, DataChangeScope.BASE);
+ LOG.info("FRM Flow Node Config Reconcil Provider started.");
+ }
+
+ @Override
+ public void close() {
+ LOG.info("FRM Flow Node Config Reconcil Provider stopped.");
+ if (flowNodeReconcilListenerRegistration != null) {
+ try {
+ flowNodeReconcilListenerRegistration.close();
+ } catch (Exception e) {
+ String errMsg = "Error by stop FRM Flow Node Config Reconcil Provider.";
+ LOG.error(errMsg, e);
+ throw new IllegalStateException(errMsg, e);
+ } finally {
+ flowNodeReconcilListenerRegistration = null;
+ }
+ }
+ }
+
+ public DataChangeListener getFlowNodeReconcilListener() {
+ return flowNodeReconcilListener;
+ }
+
+ public DataBroker getDataService() {
+ return dataService;
+ }
+
+ public SalFlowService getSalFlowService() {
+ return salFlowService;
+ }
+
+ public SalMeterService getSalMeterService() {
+ return salMeterService;
+ }
+
+ public SalGroupService getSalGroupService() {
+ return salGroupService;
+ }
+}
*/
package org.opendaylight.controller.md.inventory.manager;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.ExecutionException;
import java.util.concurrent.LinkedBlockingDeque;
-
-import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
-import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
-import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
-import org.opendaylight.yangtools.yang.common.RpcResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.base.Preconditions;
-
class FlowCapableInventoryProvider implements AutoCloseable, Runnable {
private static final Logger LOG = LoggerFactory.getLogger(FlowCapableInventoryProvider.class);
private static final int QUEUE_DEPTH = 500;
private final BlockingQueue<InventoryOperation> queue = new LinkedBlockingDeque<>(QUEUE_DEPTH);
private final NotificationProviderService notificationService;
- private final DataProviderService dataService;
+
+ private final DataBroker dataBroker;
private ListenerRegistration<?> listenerRegistration;
private Thread thread;
- FlowCapableInventoryProvider(final DataProviderService dataService, final NotificationProviderService notificationService) {
- this.dataService = Preconditions.checkNotNull(dataService);
+ FlowCapableInventoryProvider(final DataBroker dataBroker, final NotificationProviderService notificationService) {
+ this.dataBroker = Preconditions.checkNotNull(dataBroker);
this.notificationService = Preconditions.checkNotNull(notificationService);
}
@Override
public void run() {
try {
- for (;;) {
+ for (; ; ) {
InventoryOperation op = queue.take();
- final DataModificationTransaction tx = dataService.beginTransaction();
+ final ReadWriteTransaction tx = dataBroker.newReadWriteTransaction();
LOG.debug("New operations available, starting transaction {}", tx.getIdentifier());
int ops = 0;
LOG.debug("Processed {} operations, submitting transaction {}", ops, tx.getIdentifier());
- try {
- final RpcResult<TransactionStatus> result = tx.commit().get();
- if(!result.isSuccessful()) {
- LOG.error("Transaction {} failed", tx.getIdentifier());
+ final CheckedFuture<Void, TransactionCommitFailedException> result = tx.submit();
+ Futures.addCallback(result, new FutureCallback<Void>() {
+ @Override
+ public void onSuccess(Void aVoid) {
+ //NOOP
+ }
+
+ @Override
+ public void onFailure(Throwable throwable) {
+ LOG.error("Transaction {} failed.", tx.getIdentifier(), throwable);
}
- } catch (ExecutionException e) {
- LOG.warn("Failed to commit inventory change", e.getCause());
- }
+ });
}
} catch (InterruptedException e) {
LOG.info("Processing interrupted, terminating", e);
*/
package org.opendaylight.controller.md.inventory.manager;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.sal.binding.api.AbstractBindingAwareProvider;
import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
-import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Override
public void onSessionInitiated(final ProviderContext session) {
- DataProviderService salDataService = session.getSALService(DataProviderService.class);
+ DataBroker dataBroker = session.getSALService(DataBroker.class);
NotificationProviderService salNotifiService =
session.getSALService(NotificationProviderService.class);
- provider = new FlowCapableInventoryProvider(salDataService, salNotifiService);
+ provider = new FlowCapableInventoryProvider(dataBroker, salNotifiService);
provider.start();
}
*/
package org.opendaylight.controller.md.inventory.manager;
-import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
interface InventoryOperation {
- void applyOperation(DataModificationTransaction tx);
+ void applyOperation(ReadWriteTransaction tx);
}
*/
package org.opendaylight.controller.md.inventory.manager;
-import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnectorUpdated;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeUpdated;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRemoved;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorUpdated;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.base.Preconditions;
-
class NodeChangeCommiter implements OpendaylightInventoryListener {
private static final Logger LOG = LoggerFactory.getLogger(NodeChangeCommiter.class);
@Override
public synchronized void onNodeConnectorRemoved(final NodeConnectorRemoved connector) {
+ LOG.debug("Node connector removed notification received.");
manager.enqueue(new InventoryOperation() {
@Override
- public void applyOperation(final DataModificationTransaction tx) {
+ public void applyOperation(final ReadWriteTransaction tx) {
final NodeConnectorRef ref = connector.getNodeConnectorRef();
LOG.debug("removing node connector {} ", ref.getValue());
- tx.removeOperationalData(ref.getValue());
+ tx.delete(LogicalDatastoreType.OPERATIONAL, ref.getValue());
}
});
}
@Override
public synchronized void onNodeConnectorUpdated(final NodeConnectorUpdated connector) {
+ LOG.debug("Node connector updated notification received.");
manager.enqueue(new InventoryOperation() {
@Override
- public void applyOperation(final DataModificationTransaction tx) {
+ public void applyOperation(final ReadWriteTransaction tx) {
final NodeConnectorRef ref = connector.getNodeConnectorRef();
final NodeConnectorBuilder data = new NodeConnectorBuilder(connector);
data.setKey(new NodeConnectorKey(connector.getId()));
final FlowCapableNodeConnector augment = InventoryMapping.toInventoryAugment(flowConnector);
data.addAugmentation(FlowCapableNodeConnector.class, augment);
}
- InstanceIdentifier<? extends Object> value = ref.getValue();
+ InstanceIdentifier<NodeConnector> value = (InstanceIdentifier<NodeConnector>) ref.getValue();
LOG.debug("updating node connector : {}.", value);
NodeConnector build = data.build();
- tx.putOperationalData(value, build);
+ tx.put(LogicalDatastoreType.OPERATIONAL, value, build);
}
});
}
@Override
public synchronized void onNodeRemoved(final NodeRemoved node) {
+ LOG.debug("Node removed notification received.");
manager.enqueue(new InventoryOperation() {
@Override
- public void applyOperation(final DataModificationTransaction tx) {
+ public void applyOperation(final ReadWriteTransaction tx) {
final NodeRef ref = node.getNodeRef();
LOG.debug("removing node : {}", ref.getValue());
- tx.removeOperationalData((ref.getValue()));
+ tx.delete(LogicalDatastoreType.OPERATIONAL, ref.getValue());
}
});
}
if (flowNode == null) {
return;
}
-
+ LOG.debug("Node updated notification received.");
manager.enqueue(new InventoryOperation() {
@Override
- public void applyOperation(final DataModificationTransaction tx) {
+ public void applyOperation(ReadWriteTransaction tx) {
final NodeRef ref = node.getNodeRef();
+ @SuppressWarnings("unchecked")
+ InstanceIdentifierBuilder<Node> builder = ((InstanceIdentifier<Node>) ref.getValue()).builder();
+ InstanceIdentifierBuilder<FlowCapableNode> augmentation = builder.augmentation(FlowCapableNode.class);
+ final InstanceIdentifier<FlowCapableNode> path = augmentation.build();
+ CheckedFuture readFuture = tx.read(LogicalDatastoreType.OPERATIONAL, path);
+ Futures.addCallback(readFuture, new FutureCallback<Optional<? extends DataObject>>() {
+ @Override
+ public void onSuccess(Optional<? extends DataObject> optional) {
+ enqueueWriteNodeDataTx(node, flowNode, path);
+ if (!optional.isPresent()) {
+ enqueuePutTable0Tx(ref);
+ }
+ }
+
+ @Override
+ public void onFailure(Throwable throwable) {
+ LOG.debug(String.format("Can't retrieve node data for node %s. Writing node data with table0.", node));
+ enqueueWriteNodeDataTx(node, flowNode, path);
+ enqueuePutTable0Tx(ref);
+ }
+ });
+ }
+ });
+ }
+
+ private void enqueueWriteNodeDataTx(final NodeUpdated node, final FlowCapableNodeUpdated flowNode, final InstanceIdentifier<FlowCapableNode> path) {
+ manager.enqueue(new InventoryOperation() {
+ @Override
+ public void applyOperation(final ReadWriteTransaction tx) {
final NodeBuilder nodeBuilder = new NodeBuilder(node);
nodeBuilder.setKey(new NodeKey(node.getId()));
final FlowCapableNode augment = InventoryMapping.toInventoryAugment(flowNode);
nodeBuilder.addAugmentation(FlowCapableNode.class, augment);
-
- @SuppressWarnings("unchecked")
- InstanceIdentifierBuilder<Node> builder = ((InstanceIdentifier<Node>) ref.getValue()).builder();
- InstanceIdentifierBuilder<FlowCapableNode> augmentation = builder.augmentation(FlowCapableNode.class);
- final InstanceIdentifier<FlowCapableNode> path = augmentation.build();
LOG.debug("updating node :{} ", path);
- tx.putOperationalData(path, augment);
+ tx.put(LogicalDatastoreType.OPERATIONAL, path, augment);
+ }
+ });
+ }
+
+ private void enqueuePutTable0Tx(final NodeRef ref) {
+ manager.enqueue(new InventoryOperation() {
+ @Override
+ public void applyOperation(ReadWriteTransaction tx) {
+ final TableKey tKey = new TableKey((short) 0);
+ final InstanceIdentifier<Table> tableIdentifier =
+ ((InstanceIdentifier<Node>) ref.getValue()).augmentation(FlowCapableNode.class).child(Table.class, new TableKey(tKey));
+ TableBuilder tableBuilder = new TableBuilder();
+ Table table0 = tableBuilder.setId((short) 0).build();
+ LOG.debug("writing table :{} ", tableIdentifier);
+ tx.put(LogicalDatastoreType.OPERATIONAL, tableIdentifier, table0, true);
}
});
}
uses flow-node-connector;
}
+ augment "/inv:nodes/inv:node/table" {
+ ext:augment-identifier "flow-cookie-mapping";
+ list flow-cookie-map {
+ key "cookie";
+ leaf cookie {
+ type flow:flow-cookie;
+ }
+ leaf-list flow-ids {
+ type flow-id;
+ }
+ }
+ }
}
@Override
public void modifiedService(ServiceReference<BindingAwareBroker> reference, BindingAwareBroker service) {
- // TODO Auto-generated method stub
-
+ removedService(reference, service);
+ addingService(reference);
}
@Override
public void removedService(ServiceReference<BindingAwareBroker> reference, BindingAwareBroker service) {
- // TODO Auto-generated method stub
+ broker = context.getService(reference);
+ mdActivationPool.execute(new Runnable() {
+ @Override
+ public void run() {
+ onBrokerRemoved(broker, context);
+ }
+ });
}
};
protected abstract void onBrokerAvailable(BindingAwareBroker broker, BundleContext context);
protected void onBrokerRemoved(BindingAwareBroker broker, BundleContext context) {
-
+ stopImpl(context);
}
}
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-parser-impl</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-util</artifactId>
+ </dependency>
<dependency>
<groupId>xmlunit</groupId>
<artifactId>jsr305</artifactId>
<version>2.0.1</version>
</dependency>
+
+ <dependency>
+ <groupId>com.codahale.metrics</groupId>
+ <artifactId>metrics-core</artifactId>
+ <version>3.0.1</version>
+ </dependency>
</dependencies>
</project>
--- /dev/null
+/*
+ * Copyright (c) 2014 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.common.actor;
+
+import akka.actor.ActorPath;
+import akka.actor.ActorRef;
+import akka.actor.ActorSystem;
+import akka.dispatch.BoundedMailbox;
+import akka.dispatch.MailboxType;
+import akka.dispatch.MessageQueue;
+import akka.dispatch.ProducesMessageQueue;
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.MetricRegistry;
+import com.google.common.base.Preconditions;
+import com.typesafe.config.Config;
+import org.opendaylight.controller.common.reporting.MetricsReporter;
+import scala.concurrent.duration.FiniteDuration;
+
+import java.util.concurrent.TimeUnit;
+
+public class MeteredBoundedMailbox implements MailboxType, ProducesMessageQueue<BoundedMailbox.MessageQueue> {
+
+ private MeteredMessageQueue queue;
+ private Integer capacity;
+ private FiniteDuration pushTimeOut;
+ private ActorPath actorPath;
+ private MetricsReporter reporter;
+
+ private final String QUEUE_SIZE = "queue-size";
+ private final Long DEFAULT_TIMEOUT = 10L;
+
+ public MeteredBoundedMailbox(ActorSystem.Settings settings, Config config) {
+ Preconditions.checkArgument( config.hasPath("mailbox-capacity"), "Missing configuration [mailbox-capacity]" );
+ this.capacity = config.getInt("mailbox-capacity");
+ Preconditions.checkArgument( this.capacity > 0, "mailbox-capacity must be > 0");
+
+ Long timeout = -1L;
+ if ( config.hasPath("mailbox-push-timeout-time") ){
+ timeout = config.getDuration("mailbox-push-timeout-time", TimeUnit.NANOSECONDS);
+ } else {
+ timeout = DEFAULT_TIMEOUT;
+ }
+ Preconditions.checkArgument( timeout > 0, "mailbox-push-timeout-time must be > 0");
+ this.pushTimeOut = new FiniteDuration(timeout, TimeUnit.NANOSECONDS);
+
+ reporter = MetricsReporter.getInstance();
+ }
+
+
+ @Override
+ public MessageQueue create(final scala.Option<ActorRef> owner, scala.Option<ActorSystem> system) {
+ this.queue = new MeteredMessageQueue(this.capacity, this.pushTimeOut);
+ monitorQueueSize(owner, this.queue);
+ return this.queue;
+ }
+
+ private void monitorQueueSize(scala.Option<ActorRef> owner, final MeteredMessageQueue monitoredQueue) {
+ if (owner.isEmpty()) {
+ return; //there's no actor to monitor
+ }
+ actorPath = owner.get().path();
+ MetricRegistry registry = reporter.getMetricsRegistry();
+
+ String actorName = registry.name(actorPath.toString(), QUEUE_SIZE);
+
+ if (registry.getMetrics().containsKey(actorName))
+ return; //already registered
+
+ reporter.getMetricsRegistry().register(actorName,
+ new Gauge<Integer>() {
+ @Override
+ public Integer getValue() {
+ return monitoredQueue.size();
+ }
+ });
+ }
+
+
+ public static class MeteredMessageQueue extends BoundedMailbox.MessageQueue {
+
+ public MeteredMessageQueue(int capacity, FiniteDuration pushTimeOut) {
+ super(capacity, pushTimeOut);
+ }
+ }
+
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2014 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.common.reporting;
+
+import com.codahale.metrics.JmxReporter;
+import com.codahale.metrics.MetricRegistry;
+
+/**
+ * Maintains metrics registry that is provided to reporters.
+ * At the moment only one reporter exists {@code JmxReporter}.
+ * More reporters can be added.
+ * <p/>
+ * The consumers of this class will only be interested in {@code MetricsRegistry}
+ * where metrics for that consumer gets stored.
+ */
+public class MetricsReporter implements AutoCloseable{
+
+ private final MetricRegistry METRICS_REGISTRY = new MetricRegistry();
+ private final String DOMAIN = "org.opendaylight.controller";
+
+ public final JmxReporter jmxReporter = JmxReporter.forRegistry(METRICS_REGISTRY).inDomain(DOMAIN).build();
+
+ private static MetricsReporter inst = new MetricsReporter();
+
+ private MetricsReporter(){
+ jmxReporter.start();
+ }
+
+ public static MetricsReporter getInstance(){
+ return inst;
+ }
+
+ public MetricRegistry getMetricsRegistry(){
+ return METRICS_REGISTRY;
+ }
+
+ @Override
+ public void close() throws Exception {
+ jmxReporter.close();
+ }
+}
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: CompositeModificationPayload.proto
-package org.opendaylight.controller.mdsal;
+package org.opendaylight.controller.protobuff.messages.shard;
public final class CompositeModificationPayload {
private CompositeModificationPayload() {}
public static void registerAllExtensions(
com.google.protobuf.ExtensionRegistry registry) {
- registry.add(org.opendaylight.controller.mdsal.CompositeModificationPayload.modification);
+ registry.add(org.opendaylight.controller.protobuff.messages.shard.CompositeModificationPayload.modification);
}
public static final int MODIFICATION_FIELD_NUMBER = 2;
/**
"daylight.controller.cluster.raft.AppendE" +
"ntries.ReplicatedLogEntry.Payload\030\002 \001(\0132" +
"8.org.opendaylight.controller.mdsal.Comp" +
- "ositeModification"
+ "ositeModificationB6\n4org.opendaylight.co" +
+ "ntroller.protobuff.messages.shard"
};
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
* 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.remote.rpc.utils;
+package org.opendaylight.controller.xml.codec;
import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;
* 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.remote.rpc.utils;
+package org.opendaylight.controller.xml.codec;
import org.opendaylight.yangtools.yang.common.QName;
* 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.remote.rpc.utils;
+package org.opendaylight.controller.xml.codec;
import com.google.common.base.Function;
import com.google.common.base.Objects;
* 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.remote.rpc.utils;
+package org.opendaylight.controller.xml.codec;
import com.google.common.annotations.Beta;
import com.google.common.base.Preconditions;
* 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.remote.rpc.utils;
+package org.opendaylight.controller.xml.codec;
import com.google.common.base.Optional;
import org.opendaylight.controller.netconf.util.xml.XmlUtil;
import "Common.proto";
import "Persistent.proto";
+option java_package = "org.opendaylight.controller.protobuff.messages.shard";
+
extend org.opendaylight.controller.cluster.raft.AppendEntries.ReplicatedLogEntry.Payload {
optional CompositeModification modification=2;
}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.common.actor;
+
+import akka.actor.ActorRef;
+import akka.actor.ActorSystem;
+import akka.actor.DeadLetter;
+import akka.actor.Props;
+import akka.actor.UntypedActor;
+import akka.japi.Creator;
+import akka.testkit.JavaTestKit;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import scala.concurrent.duration.FiniteDuration;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class MeteredBoundedMailboxTest {
+
+ private static ActorSystem actorSystem;
+ private final ReentrantLock lock = new ReentrantLock();
+
+ @Before
+ public void setUp() throws Exception {
+ actorSystem = ActorSystem.create("testsystem");
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (actorSystem != null)
+ actorSystem.shutdown();
+ }
+
+ @Test
+ public void test_WhenQueueIsFull_ShouldSendMsgToDeadLetter() throws InterruptedException {
+ final JavaTestKit mockReceiver = new JavaTestKit(actorSystem);
+ actorSystem.eventStream().subscribe(mockReceiver.getRef(), DeadLetter.class);
+
+
+ final FiniteDuration ONE_SEC = new FiniteDuration(1, TimeUnit.SECONDS);
+ String boundedMailBox = actorSystem.name() + ".bounded-mailbox";
+ ActorRef pingPongActor = actorSystem.actorOf(PingPongActor.props(lock).withMailbox(boundedMailBox),
+ "pingpongactor");
+
+ actorSystem.mailboxes().settings();
+ lock.lock();
+ //queue capacity = 10
+ //need to send 12 messages; 1 message is dequeued and actor waits on lock,
+ //2nd to 11th messages are put on the queue
+ //12th message is sent to dead letter.
+ for (int i=0;i<12;i++){
+ pingPongActor.tell("ping", mockReceiver.getRef());
+ }
+
+ mockReceiver.expectMsgClass(ONE_SEC, DeadLetter.class);
+
+ lock.unlock();
+
+ Object[] eleven = mockReceiver.receiveN(11, ONE_SEC);
+ }
+
+ /**
+ * For testing
+ */
+ public static class PingPongActor extends UntypedActor{
+
+ ReentrantLock lock;
+
+ private PingPongActor(ReentrantLock lock){
+ this.lock = lock;
+ }
+
+ public static Props props(final ReentrantLock lock){
+ return Props.create(new Creator<PingPongActor>(){
+ @Override
+ public PingPongActor create() throws Exception {
+ return new PingPongActor(lock);
+ }
+ });
+ }
+
+ @Override
+ public void onReceive(Object message) throws Exception {
+ lock.lock();
+ if ("ping".equals(message))
+ getSender().tell("pong", getSelf());
+ }
+ }
+}
\ No newline at end of file
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-package org.opendaylight.controller.remote.rpc.utils;
+package org.opendaylight.controller.xml.codec;
import com.google.common.collect.ImmutableList;
+testsystem {
+
+ bounded-mailbox {
+ mailbox-type = "org.opendaylight.controller.common.actor.MeteredBoundedMailbox"
+ mailbox-capacity = 10
+ mailbox-push-timeout-time = 100ms
+ }
+}
\ No newline at end of file
--- /dev/null
+testsystem {
+
+ bounded-mailbox {
+ mailbox-type = "org.opendaylight.controller.common.actor.MeteredBoundedMailbox"
+ mailbox-capacity = 1000
+ mailbox-push-timeout-time = 10ms
+ }
+}
\ No newline at end of file
<description>Configuration files for md-sal clustering</description>
<packaging>jar</packaging>
<build>
- <plugins>
- <plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>build-helper-maven-plugin</artifactId>
- <executions>
- <execution>
- <id>attach-artifacts</id>
- <goals>
- <goal>attach-artifact</goal>
- </goals>
- <phase>package</phase>
- <configuration>
- <artifacts>
- <artifact>
- <file>${project.build.directory}/classes/initial/*.conf</file>
- <type>xml</type>
- <classifier>config</classifier>
- </artifact>
- </artifacts>
- </configuration>
- </execution>
- </executions>
- </plugin>
- </plugins>
</build>
</project>
<version>1.1-SNAPSHOT</version>
</dependency>
+ <dependency>
+ <groupId>com.codahale.metrics</groupId>
+ <artifactId>metrics-core</artifactId>
+ <version>3.0.1</version>
+ </dependency>
<!-- Test Dependencies -->
<dependency>
<groupId>junit</groupId>
<Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name>
<Export-package></Export-package>
<Private-Package></Private-Package>
- <Import-Package>!*snappy;!org.jboss.*;*</Import-Package>
+ <Import-Package>!*snappy;!org.jboss.*;!com.jcraft.*;*</Import-Package>
<Embed-Dependency>
sal-clustering-commons;
sal-akka-raft;
+ *metrics*;
!sal*;
!*config-api*;
!*testkit*;
Logging.getLogger(getContext().system(), this);
- public AbstractUntypedActor(){
+ public AbstractUntypedActor() {
LOG.debug("Actor created {}", getSelf());
getContext().
system().
@Override public void onReceive(Object message) throws Exception {
LOG.debug("Received message {}", message.getClass().getSimpleName());
handleReceive(message);
- LOG.debug("Done handling message {}", message.getClass().getSimpleName());
+ LOG.debug("Done handling message {}",
+ message.getClass().getSimpleName());
}
protected abstract void handleReceive(Object message) throws Exception;
- protected void ignoreMessage(Object message){
+ protected void ignoreMessage(Object message) {
LOG.debug("Unhandled message {} ", message);
}
- protected void unknownMessage(Object message) throws Exception{
+ protected void unknownMessage(Object message) throws Exception {
+ LOG.debug("Received unhandled message {}", message);
unhandled(message);
}
}
@Override public Map<GeneratedMessage.GeneratedExtension, PersistentMessages.CompositeModification> encode() {
Preconditions.checkState(modification!=null);
Map<GeneratedMessage.GeneratedExtension, PersistentMessages.CompositeModification> map = new HashMap<>();
- map.put(org.opendaylight.controller.mdsal.CompositeModificationPayload.modification, this.modification);
+ map.put(
+ org.opendaylight.controller.protobuff.messages.shard.CompositeModificationPayload.modification, this.modification);
return map;
}
AppendEntriesMessages.AppendEntries.ReplicatedLogEntry.Payload payload) {
PersistentMessages.CompositeModification modification = payload
.getExtension(
- org.opendaylight.controller.mdsal.CompositeModificationPayload.modification);
+ org.opendaylight.controller.protobuff.messages.shard.CompositeModificationPayload.modification);
import akka.serialization.Serialization;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import org.opendaylight.controller.cluster.datastore.identifiers.ShardIdentifier;
import org.opendaylight.controller.cluster.datastore.identifiers.ShardTransactionIdentifier;
}
- private static Map<String, String> mapPeerAddresses(Map<ShardIdentifier, String> peerAddresses){
- Map<String , String> map = new HashMap<>();
+ private static Map<String, String> mapPeerAddresses(
+ Map<ShardIdentifier, String> peerAddresses) {
+ Map<String, String> map = new HashMap<>();
- for(Map.Entry<ShardIdentifier, String> entry : peerAddresses.entrySet()){
+ for (Map.Entry<ShardIdentifier, String> entry : peerAddresses
+ .entrySet()) {
map.put(entry.getKey().toString(), entry.getValue());
}
return map;
}
+
+
+
public static Props props(final ShardIdentifier name,
final Map<ShardIdentifier, String> peerAddresses,
final InMemoryDOMDataStoreConfigProperties dataStoreProperties) {
Preconditions.checkNotNull(name, "name should not be null");
- Preconditions.checkNotNull(peerAddresses, "peerAddresses should not be null");
+ Preconditions
+ .checkNotNull(peerAddresses, "peerAddresses should not be null");
return Props.create(new Creator<Shard>() {
}
} else if (message instanceof PeerAddressResolved) {
PeerAddressResolved resolved = (PeerAddressResolved) message;
- setPeerAddress(resolved.getPeerId().toString(), resolved.getPeerAddress());
+ setPeerAddress(resolved.getPeerId().toString(),
+ resolved.getPeerAddress());
} else {
super.onReceiveCommand(message);
}
}
private ActorRef createTypedTransactionActor(
- CreateTransaction createTransaction, ShardTransactionIdentifier transactionId) {
+ CreateTransaction createTransaction,
+ ShardTransactionIdentifier transactionId) {
if (createTransaction.getTransactionType()
== TransactionProxy.TransactionType.READ_ONLY.ordinal()) {
.props(store.newWriteOnlyTransaction(), getSelf(),
schemaContext), transactionId.toString());
} else {
- // FIXME: This does not seem right
throw new IllegalArgumentException(
- "CreateTransaction message has unidentified transaction type="
+ "Shard="+name + ":CreateTransaction message has unidentified transaction type="
+ createTransaction.getTransactionType());
}
}
private void createTransaction(CreateTransaction createTransaction) {
- ShardTransactionIdentifier transactionId = ShardTransactionIdentifier.builder().remoteTransactionId(createTransaction.getTransactionId()).build();
+ ShardTransactionIdentifier transactionId =
+ ShardTransactionIdentifier.builder()
+ .remoteTransactionId(createTransaction.getTransactionId())
+ .build();
LOG.debug("Creating transaction : {} ", transactionId);
ActorRef transactionActor =
createTypedTransactionActor(createTransaction, transactionId);
getSender()
.tell(new CreateTransactionReply(
- Serialization.serializedActorPath(transactionActor),
- createTransaction.getTransactionId()).toSerializable(),
+ Serialization.serializedActorPath(transactionActor),
+ createTransaction.getTransactionId()).toSerializable(),
getSelf());
}
final ListenableFuture<Void> future = cohort.commit();
final ActorRef self = getSelf();
- future.addListener(new Runnable() {
- @Override
- public void run() {
- try {
- future.get();
- sender
- .tell(new CommitTransactionReply().toSerializable(),
- self);
- shardMBean.incrementCommittedTransactionCount();
- shardMBean.setLastCommittedTransactionTime(new Date());
- } catch (InterruptedException | ExecutionException e) {
- shardMBean.incrementFailedTransactionsCount();
- sender.tell(new akka.actor.Status.Failure(e),self);
- }
+
+ Futures.addCallback(future, new FutureCallback<Void>() {
+ public void onSuccess(Void v) {
+ sender.tell(new CommitTransactionReply().toSerializable(),self);
+ shardMBean.incrementCommittedTransactionCount();
+ shardMBean.setLastCommittedTransactionTime(new Date());
}
- }, getContext().dispatcher());
+
+ public void onFailure(Throwable t) {
+ LOG.error(t, "An exception happened during commit");
+ shardMBean.incrementFailedTransactionsCount();
+ sender.tell(new akka.actor.Status.Failure(t), self);
+ }
+ });
+
}
private void handleForwardedCommit(ForwardedCommitTransaction message) {
LOG.debug(
"registerDataChangeListener sending reply, listenerRegistrationPath = {} "
- , listenerRegistration.path().toString());
+ , listenerRegistration.path().toString());
getSender()
.tell(new RegisterChangeListenerReply(listenerRegistration.path()),
// Update stats
ReplicatedLogEntry lastLogEntry = getLastLogEntry();
- if(lastLogEntry != null){
+ if (lastLogEntry != null) {
shardMBean.setLastLogIndex(lastLogEntry.getIndex());
shardMBean.setLastLogTerm(lastLogEntry.getTerm());
}
@Override
public SupervisorStrategy supervisorStrategy() {
+
return new OneForOneStrategy(10, Duration.create("1 minute"),
new Function<Throwable, SupervisorStrategy.Directive>() {
@Override
public SupervisorStrategy.Directive apply(Throwable t) {
+ LOG.warning("Supervisor Strategy of resume applied {}",t);
return SupervisorStrategy.resume();
}
}
import akka.event.Logging;
import akka.event.LoggingAdapter;
import akka.japi.Creator;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import org.opendaylight.controller.cluster.datastore.messages.AbortTransaction;
import org.opendaylight.controller.cluster.datastore.messages.AbortTransactionReply;
import org.opendaylight.controller.cluster.datastore.modification.CompositeModification;
import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
-import java.util.concurrent.ExecutionException;
-
public class ThreePhaseCommitCohort extends AbstractUntypedActor {
private final DOMStoreThreePhaseCommitCohort cohort;
private final ActorRef shardActor;
@Override
public void handleReceive(Object message) throws Exception {
- if (message.getClass().equals(CanCommitTransaction.SERIALIZABLE_CLASS)) {
+ if (message.getClass()
+ .equals(CanCommitTransaction.SERIALIZABLE_CLASS)) {
canCommit(new CanCommitTransaction());
- } else if (message.getClass().equals(PreCommitTransaction.SERIALIZABLE_CLASS)) {
+ } else if (message.getClass()
+ .equals(PreCommitTransaction.SERIALIZABLE_CLASS)) {
preCommit(new PreCommitTransaction());
- } else if (message.getClass().equals(CommitTransaction.SERIALIZABLE_CLASS)) {
+ } else if (message.getClass()
+ .equals(CommitTransaction.SERIALIZABLE_CLASS)) {
commit(new CommitTransaction());
- } else if (message.getClass().equals(AbortTransaction.SERIALIZABLE_CLASS)) {
+ } else if (message.getClass()
+ .equals(AbortTransaction.SERIALIZABLE_CLASS)) {
abort(new AbortTransaction());
} else {
unknownMessage(message);
final ActorRef sender = getSender();
final ActorRef self = getSelf();
- future.addListener(new Runnable() {
- @Override
- public void run() {
- try {
- future.get();
- sender.tell(new AbortTransactionReply().toSerializable(), self);
- } catch (InterruptedException | ExecutionException e) {
- log.error(e, "An exception happened when aborting");
- }
+ Futures.addCallback(future, new FutureCallback<Void>() {
+ public void onSuccess(Void v) {
+ sender
+ .tell(new AbortTransactionReply().toSerializable(),
+ self);
+ }
+
+ public void onFailure(Throwable t) {
+ LOG.error(t, "An exception happened during abort");
+ sender
+ .tell(new akka.actor.Status.Failure(t), getSelf());
}
- }, getContext().dispatcher());
+ });
}
private void commit(CommitTransaction message) {
final ListenableFuture<Void> future = cohort.preCommit();
final ActorRef sender = getSender();
final ActorRef self = getSelf();
+ Futures.addCallback(future, new FutureCallback<Void>() {
+ public void onSuccess(Void v) {
+ sender
+ .tell(new PreCommitTransactionReply().toSerializable(),
+ self);
+ }
- future.addListener(new Runnable() {
- @Override
- public void run() {
- try {
- future.get();
- sender.tell(new PreCommitTransactionReply().toSerializable(), self);
- } catch (InterruptedException | ExecutionException e) {
- log.error(e, "An exception happened when preCommitting");
- }
+ public void onFailure(Throwable t) {
+ LOG.error(t, "An exception happened during pre-commit");
+ sender
+ .tell(new akka.actor.Status.Failure(t), getSelf());
}
- }, getContext().dispatcher());
+ });
}
final ListenableFuture<Boolean> future = cohort.canCommit();
final ActorRef sender = getSender();
final ActorRef self = getSelf();
+ Futures.addCallback(future, new FutureCallback<Boolean>() {
+ public void onSuccess(Boolean canCommit) {
+ sender.tell(new CanCommitTransactionReply(canCommit)
+ .toSerializable(), self);
+ }
- future.addListener(new Runnable() {
- @Override
- public void run() {
- try {
- Boolean canCommit = future.get();
- sender.tell(new CanCommitTransactionReply(canCommit).toSerializable(), self);
- } catch (InterruptedException | ExecutionException e) {
- log.error(e, "An exception happened when checking canCommit");
- }
+ public void onFailure(Throwable t) {
+ LOG.error(t, "An exception happened during canCommit");
+ sender
+ .tell(new akka.actor.Status.Failure(t), getSelf());
}
- }, getContext().dispatcher());
+ });
+
}
}
/**
* Covers negative test cases
+ *
* @author Basheeruddin Ahmed <syedbahm@cisco.com>
*/
public class ShardTransactionFailureTest extends AbstractActorTest {
private static final ShardIdentifier SHARD_IDENTIFIER =
ShardIdentifier.builder().memberName("member-1")
- .shardName("inventory").type("config").build();
+ .shardName("inventory").type("operational").build();
static {
store.onGlobalContextUpdated(testSchemaContext);
throws Throwable {
final ActorRef shard =
- getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, null));
+ getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP,null));
final Props props =
ShardTransaction.props(store.newReadWriteTransaction(), shard,
TestModel.createTestContext());
throws Throwable {
final ActorRef shard =
- getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, null));
+ getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP,null));
final Props props =
ShardTransaction.props(store.newReadWriteTransaction(), shard,
TestModel.createTestContext());
final ActorRef shard =
- getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, null));
+ getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP,null));
final Props props =
ShardTransaction.props(store.newWriteOnlyTransaction(), shard,
TestModel.createTestContext());
final ActorRef shard =
- getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, null));
+ getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP,null));
final Props props =
ShardTransaction.props(store.newReadWriteTransaction(), shard,
TestModel.createTestContext());
final ActorRef shard =
- getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, null));
+ getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP,null));
final Props props =
ShardTransaction.props(store.newReadWriteTransaction(), shard,
TestModel.createTestContext());
final ActorRef shard =
- getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP, null));
+ getSystem().actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP,null));
final Props props =
ShardTransaction.props(store.newReadWriteTransaction(), shard,
TestModel.createTestContext());
--- /dev/null
+/*
+ *
+ * Copyright (c) 2014 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.cluster.datastore;
+
+import akka.actor.ActorRef;
+import akka.actor.Props;
+import akka.testkit.TestActorRef;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.opendaylight.controller.cluster.datastore.identifiers.ShardIdentifier;
+import org.opendaylight.controller.cluster.datastore.messages.ForwardedCommitTransaction;
+import org.opendaylight.controller.cluster.datastore.modification.CompositeModification;
+import org.opendaylight.controller.cluster.datastore.modification.Modification;
+import org.opendaylight.controller.md.cluster.datastore.model.TestModel;
+import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException;
+import org.opendaylight.controller.md.sal.dom.store.impl.InMemoryDOMDataStore;
+import org.opendaylight.controller.protobuff.messages.cohort3pc.ThreePhaseCommitCohortMessages;
+import org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages;
+import org.opendaylight.controller.protobuff.messages.persistent.PersistentMessages;
+import org.opendaylight.controller.protobuff.messages.transaction.ShardTransactionMessages;
+import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import scala.concurrent.Await;
+import scala.concurrent.Future;
+import scala.concurrent.duration.Duration;
+import scala.concurrent.duration.FiniteDuration;
+
+import java.util.Collections;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+
+public class ThreePhaseCommitCohortFailureTest extends AbstractActorTest {
+
+ private static ListeningExecutorService storeExecutor =
+ MoreExecutors.listeningDecorator(MoreExecutors.sameThreadExecutor());
+
+ private static final InMemoryDOMDataStore store =
+ new InMemoryDOMDataStore("OPER", storeExecutor,
+ MoreExecutors.sameThreadExecutor());
+
+ private static final SchemaContext testSchemaContext =
+ TestModel.createTestContext();
+
+ private static final ShardIdentifier SHARD_IDENTIFIER =
+ ShardIdentifier.builder().memberName("member-1")
+ .shardName("inventory").type("config").build();
+
+ static {
+ store.onGlobalContextUpdated(testSchemaContext);
+ }
+
+ private FiniteDuration ASK_RESULT_DURATION = Duration.create(3000, TimeUnit.MILLISECONDS);
+
+
+ @Test(expected = TestException.class)
+ public void testNegativeAbortResultsInException() throws Exception {
+
+ final ActorRef shard =
+ getSystem()
+ .actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP,null));
+ final DOMStoreThreePhaseCommitCohort mockCohort = Mockito
+ .mock(DOMStoreThreePhaseCommitCohort.class);
+ final CompositeModification mockComposite =
+ Mockito.mock(CompositeModification.class);
+ final Props props =
+ ThreePhaseCommitCohort.props(mockCohort, shard, mockComposite);
+
+ final TestActorRef<ThreePhaseCommitCohort> subject = TestActorRef
+ .create(getSystem(), props,
+ "testNegativeAbortResultsInException");
+
+ when(mockCohort.abort()).thenReturn(
+ Futures.<Void>immediateFailedFuture(new TestException()));
+
+ Future<Object> future =
+ akka.pattern.Patterns.ask(subject,
+ ThreePhaseCommitCohortMessages.AbortTransaction.newBuilder()
+ .build(), 3000);
+ assertTrue(future.isCompleted());
+
+ Await.result(future, ASK_RESULT_DURATION);
+
+
+
+ }
+
+
+ @Test(expected = OptimisticLockFailedException.class)
+ public void testNegativeCanCommitResultsInException() throws Exception {
+
+ final ActorRef shard =
+ getSystem()
+ .actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP,null));
+ final DOMStoreThreePhaseCommitCohort mockCohort = Mockito
+ .mock(DOMStoreThreePhaseCommitCohort.class);
+ final CompositeModification mockComposite =
+ Mockito.mock(CompositeModification.class);
+ final Props props =
+ ThreePhaseCommitCohort.props(mockCohort, shard, mockComposite);
+
+ final TestActorRef<ThreePhaseCommitCohort> subject = TestActorRef
+ .create(getSystem(), props,
+ "testNegativeCanCommitResultsInException");
+
+ when(mockCohort.canCommit()).thenReturn(
+ Futures
+ .<Boolean>immediateFailedFuture(
+ new OptimisticLockFailedException("some exception")));
+
+ Future<Object> future =
+ akka.pattern.Patterns.ask(subject,
+ ThreePhaseCommitCohortMessages.CanCommitTransaction.newBuilder()
+ .build(), 3000);
+
+
+ Await.result(future, ASK_RESULT_DURATION);
+
+ }
+
+
+ @Test(expected = TestException.class)
+ public void testNegativePreCommitResultsInException() throws Exception {
+
+ final ActorRef shard =
+ getSystem()
+ .actorOf(Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP,null));
+ final DOMStoreThreePhaseCommitCohort mockCohort = Mockito
+ .mock(DOMStoreThreePhaseCommitCohort.class);
+ final CompositeModification mockComposite =
+ Mockito.mock(CompositeModification.class);
+ final Props props =
+ ThreePhaseCommitCohort.props(mockCohort, shard, mockComposite);
+
+ final TestActorRef<ThreePhaseCommitCohort> subject = TestActorRef
+ .create(getSystem(), props,
+ "testNegativePreCommitResultsInException");
+
+ when(mockCohort.preCommit()).thenReturn(
+ Futures
+ .<Void>immediateFailedFuture(
+ new TestException()));
+
+ Future<Object> future =
+ akka.pattern.Patterns.ask(subject,
+ ThreePhaseCommitCohortMessages.PreCommitTransaction.newBuilder()
+ .build(), 3000);
+
+ Await.result(future, ASK_RESULT_DURATION);
+
+ }
+
+ @Test(expected = TestException.class)
+ public void testNegativeCommitResultsInException() throws Exception {
+
+ final TestActorRef<Shard> subject = TestActorRef
+ .create(getSystem(),
+ Shard.props(SHARD_IDENTIFIER, Collections.EMPTY_MAP,null),
+ "testNegativeCommitResultsInException");
+
+ final ActorRef shardTransaction =
+ getSystem().actorOf(
+ ShardTransaction.props(store.newReadWriteTransaction(), subject,
+ TestModel.createTestContext()));
+
+ ShardTransactionMessages.WriteData writeData =
+ ShardTransactionMessages.WriteData.newBuilder()
+ .setInstanceIdentifierPathArguments(
+ NormalizedNodeMessages.InstanceIdentifier.newBuilder()
+ .build()).setNormalizedNode(
+ NormalizedNodeMessages.Node.newBuilder().build()
+
+ ).build();
+
+ //This is done so that Modification list is updated which is used during commit
+ Future future =
+ akka.pattern.Patterns.ask(shardTransaction, writeData, 3000);
+
+ //ready transaction creates the cohort so that we get into the
+ //block where in commmit is done
+ ShardTransactionMessages.ReadyTransaction readyTransaction =
+ ShardTransactionMessages.ReadyTransaction.newBuilder().build();
+
+ future =
+ akka.pattern.Patterns.ask(shardTransaction, readyTransaction, 3000);
+
+ //but when the message is sent it will have the MockCommit object
+ //so that we can simulate throwing of exception
+ ForwardedCommitTransaction mockForwardCommitTransaction =
+ Mockito.mock(ForwardedCommitTransaction.class);
+ DOMStoreThreePhaseCommitCohort mockThreePhaseCommitTransaction =
+ Mockito.mock(DOMStoreThreePhaseCommitCohort.class);
+ when(mockForwardCommitTransaction.getCohort())
+ .thenReturn(mockThreePhaseCommitTransaction);
+ when(mockThreePhaseCommitTransaction.commit()).thenReturn(Futures
+ .<Void>immediateFailedFuture(
+ new TestException()));
+ Modification mockModification = Mockito.mock(
+ Modification.class);
+ when(mockForwardCommitTransaction.getModification())
+ .thenReturn(mockModification);
+
+ when(mockModification.toSerializable()).thenReturn(
+ PersistentMessages.CompositeModification.newBuilder().build());
+
+ future =
+ akka.pattern.Patterns.ask(subject,
+ mockForwardCommitTransaction
+ , 3000);
+ Await.result(future, ASK_RESULT_DURATION);
+
+
+ }
+
+ private class TestException extends Exception {
+ }
+
+
+}
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>sal-core-api</artifactId>
- </dependency>
- <dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>netconf-util</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-core-spi</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-core-spi</artifactId>
</dependency>
<dependency>
- <groupId>org.opendaylight.controller</groupId>
- <artifactId>sal-common-impl</artifactId>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-common-impl</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-util</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>sal-clustering-commons</artifactId>
+ </dependency>
+
<!-- Yang tools-->
<dependency>
<Bundle-Name>${project.groupId}.${project.artifactId}</Bundle-Name>
<Export-package></Export-package>
<Private-Package></Private-Package>
- <Import-Package>!org.jboss.*;!com.jcraft.*;*</Import-Package>
+ <Import-Package>!org.iq80.*;!*snappy;!org.jboss.*;!com.jcraft.*;!org.fusesource.*;*</Import-Package>
<Embed-Dependency>
+ sal-clustering-commons;
+ sal-akka-raft;
!sal*;
!*config-api*;
!*testkit*;
</execution>
</executions>
</plugin>
+
+
+
</plugins>
</build>
<scm>
public static final String RPC_BROKER_PATH= "/user/rpc/rpc-broker";
public static final String RPC_REGISTRY_PATH = "/user/rpc/rpc-registry";
+ public static final String RPC_MANAGER_PATH = "/user/rpc";
}
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import org.opendaylight.controller.remote.rpc.messages.ErrorResponse;
-import org.opendaylight.controller.remote.rpc.messages.InvokeRoutedRpc;
import org.opendaylight.controller.remote.rpc.messages.InvokeRpc;
import org.opendaylight.controller.remote.rpc.messages.RpcResponse;
import org.opendaylight.controller.remote.rpc.utils.ActorUtil;
-import org.opendaylight.controller.remote.rpc.utils.XmlUtils;
+import org.opendaylight.controller.xml.codec.XmlUtils;
import org.opendaylight.controller.sal.core.api.RoutedRpcDefaultImplementation;
import org.opendaylight.controller.sal.core.api.RpcImplementation;
import org.opendaylight.yangtools.yang.common.QName;
@Override
public ListenableFuture<RpcResult<CompositeNode>> invokeRpc(QName rpc, YangInstanceIdentifier identifier, CompositeNode input) {
- InvokeRoutedRpc rpcMsg = new InvokeRoutedRpc(rpc, identifier, input);
+ InvokeRpc rpcMsg = new InvokeRpc(rpc, identifier, input);
return executeMsg(rpcMsg);
}
@Override
public ListenableFuture<RpcResult<CompositeNode>> invokeRpc(QName rpc, CompositeNode input) {
- InvokeRpc rpcMsg = new InvokeRpc(rpc, input);
+ InvokeRpc rpcMsg = new InvokeRpc(rpc, null, input);
return executeMsg(rpcMsg);
}
ListenableFuture<RpcResult<CompositeNode>> listenableFuture = null;
try {
- Object response = ActorUtil.executeLocalOperation(rpcBroker, rpcMsg, ActorUtil.ASK_DURATION, ActorUtil.AWAIT_DURATION);
+ Object response = ActorUtil.executeOperation(rpcBroker, rpcMsg, ActorUtil.ASK_DURATION, ActorUtil.AWAIT_DURATION);
if(response instanceof RpcResponse) {
RpcResponse rpcResponse = (RpcResponse) response;
}
} catch (Exception e) {
- LOG.error("Error occurred while invoking RPC actor {}", e.toString());
+ LOG.error("Error occurred while invoking RPC actor {}", e);
final RpcResultBuilder<CompositeNode> failed = RpcResultBuilder.failed();
failed.withError(null, null, e.getMessage(), null, null, e.getCause());
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import org.opendaylight.controller.remote.rpc.messages.UpdateSchemaContext;
-import org.opendaylight.controller.remote.rpc.registry.ClusterWrapper;
-import org.opendaylight.controller.remote.rpc.registry.ClusterWrapperImpl;
import org.opendaylight.controller.sal.core.api.Broker;
import org.opendaylight.controller.sal.core.api.Provider;
import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
private void start() {
LOG.info("Starting all rpc listeners and actors.");
// Create actor to handle and sync routing table in cluster
- ClusterWrapper clusterWrapper = new ClusterWrapperImpl(actorSystem);
SchemaService schemaService = brokerSession.getService(SchemaService.class);
schemaContext = schemaService.getGlobalContext();
- rpcManager = actorSystem.actorOf(RpcManager.props(clusterWrapper, schemaContext, brokerSession, rpcProvisionRegistry), ActorConstants.RPC_MANAGER);
+ rpcManager = actorSystem.actorOf(RpcManager.props(schemaContext, brokerSession, rpcProvisionRegistry), ActorConstants.RPC_MANAGER);
LOG.debug("Rpc actors are created.");
}
import com.google.common.base.Preconditions;
import org.opendaylight.controller.md.sal.common.api.routing.RouteChange;
import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener;
-import org.opendaylight.controller.remote.rpc.messages.AddRoutedRpc;
-import org.opendaylight.controller.remote.rpc.messages.RemoveRoutedRpc;
-import org.opendaylight.controller.remote.rpc.utils.ActorUtil;
+import org.opendaylight.controller.remote.rpc.registry.RpcRegistry;
import org.opendaylight.controller.sal.connector.api.RpcRouter;
import org.opendaylight.controller.sal.core.api.RpcRoutingContext;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.ArrayList;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class RoutedRpcListener implements RouteChangeListener<RpcRoutingContext, YangInstanceIdentifier>{
private static final Logger LOG = LoggerFactory.getLogger(RoutedRpcListener.class);
private final ActorRef rpcRegistry;
- private final String actorPath;
- public RoutedRpcListener(ActorRef rpcRegistry, String actorPath) {
+ public RoutedRpcListener(ActorRef rpcRegistry) {
Preconditions.checkNotNull(rpcRegistry, "rpc registry actor should not be null");
- Preconditions.checkNotNull(actorPath, "actor path of rpc broker on current node should not be null");
this.rpcRegistry = rpcRegistry;
- this.actorPath = actorPath;
}
@Override
public void onRouteChange(RouteChange<RpcRoutingContext, YangInstanceIdentifier> routeChange) {
Map<RpcRoutingContext, Set<YangInstanceIdentifier>> announcements = routeChange.getAnnouncements();
- announce(getRouteIdentifiers(announcements));
+ if(announcements != null && announcements.size() > 0){
+ announce(getRouteIdentifiers(announcements));
+ }
Map<RpcRoutingContext, Set<YangInstanceIdentifier>> removals = routeChange.getRemovals();
- remove(getRouteIdentifiers(removals));
+ if(removals != null && removals.size() > 0 ) {
+ remove(getRouteIdentifiers(removals));
+ }
}
/**
*/
private void announce(Set<RpcRouter.RouteIdentifier<?, ?, ?>> announcements) {
LOG.debug("Announcing [{}]", announcements);
- AddRoutedRpc addRpcMsg = new AddRoutedRpc(announcements, actorPath);
- try {
- ActorUtil.executeLocalOperation(rpcRegistry, addRpcMsg, ActorUtil.LOCAL_ASK_DURATION, ActorUtil.LOCAL_AWAIT_DURATION);
- } catch (Exception e) {
- // Just logging it because Akka API throws this exception
- LOG.error(e.toString());
- }
+ RpcRegistry.Messages.AddOrUpdateRoutes addRpcMsg = new RpcRegistry.Messages.AddOrUpdateRoutes(new ArrayList<>(announcements));
+ rpcRegistry.tell(addRpcMsg, ActorRef.noSender());
}
/**
*/
private void remove(Set<RpcRouter.RouteIdentifier<?, ?, ?>> removals){
LOG.debug("Removing [{}]", removals);
- RemoveRoutedRpc removeRpcMsg = new RemoveRoutedRpc(removals, actorPath);
- try {
- ActorUtil.executeLocalOperation(rpcRegistry, removeRpcMsg, ActorUtil.LOCAL_ASK_DURATION, ActorUtil.LOCAL_AWAIT_DURATION);
- } catch (Exception e) {
- // Just logging it because Akka API throws this exception
- LOG.error(e.toString());
- }
+ RpcRegistry.Messages.RemoveRoutes removeRpcMsg = new RpcRegistry.Messages.RemoveRoutes(new ArrayList<>(removals));
+ rpcRegistry.tell(removeRpcMsg, ActorRef.noSender());
}
/**
import akka.actor.ActorRef;
import akka.actor.Props;
import akka.japi.Creator;
+import akka.japi.Pair;
import org.opendaylight.controller.remote.rpc.messages.ErrorResponse;
import org.opendaylight.controller.remote.rpc.messages.ExecuteRpc;
-import org.opendaylight.controller.remote.rpc.messages.GetRoutedRpc;
-import org.opendaylight.controller.remote.rpc.messages.GetRoutedRpcReply;
-import org.opendaylight.controller.remote.rpc.messages.GetRpc;
-import org.opendaylight.controller.remote.rpc.messages.GetRpcReply;
-import org.opendaylight.controller.remote.rpc.messages.InvokeRoutedRpc;
import org.opendaylight.controller.remote.rpc.messages.InvokeRpc;
import org.opendaylight.controller.remote.rpc.messages.RpcResponse;
+import org.opendaylight.controller.remote.rpc.utils.LatestEntryRoutingLogic;
+import org.opendaylight.controller.remote.rpc.registry.RpcRegistry;
import org.opendaylight.controller.remote.rpc.utils.ActorUtil;
-import org.opendaylight.controller.remote.rpc.utils.XmlUtils;
+import org.opendaylight.controller.remote.rpc.utils.RoutingLogic;
+import org.opendaylight.controller.xml.codec.XmlUtils;
+import org.opendaylight.controller.sal.connector.api.RpcRouter;
import org.opendaylight.controller.sal.core.api.Broker;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.List;
import java.util.concurrent.Future;
/**
}
@Override
protected void handleReceive(Object message) throws Exception {
- if(message instanceof InvokeRoutedRpc) {
- invokeRemoteRoutedRpc((InvokeRoutedRpc) message);
- } else if(message instanceof InvokeRpc) {
+ if(message instanceof InvokeRpc) {
invokeRemoteRpc((InvokeRpc) message);
} else if(message instanceof ExecuteRpc) {
executeRpc((ExecuteRpc) message);
}
}
- private void invokeRemoteRoutedRpc(InvokeRoutedRpc msg) {
- // Look up the remote actor to execute rpc
- LOG.debug("Looking up the remote actor for route {}", msg);
- try {
- RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, msg.getRpc(), msg.getIdentifier());
- GetRoutedRpc routedRpcMsg = new GetRoutedRpc(routeId);
- GetRoutedRpcReply rpcReply = (GetRoutedRpcReply) ActorUtil.executeLocalOperation(rpcRegistry, routedRpcMsg, ActorUtil.LOCAL_ASK_DURATION, ActorUtil.LOCAL_AWAIT_DURATION);
-
- String remoteActorPath = rpcReply.getRoutePath();
- if(remoteActorPath == null) {
- LOG.debug("No remote actor found for rpc execution.");
-
- getSender().tell(new ErrorResponse(
- new IllegalStateException("No remote actor found for rpc execution.")), self());
- } else {
-
- ExecuteRpc executeMsg = new ExecuteRpc(XmlUtils.inputCompositeNodeToXml(msg.getInput(), schemaContext), msg.getRpc());
-
- Object operationRes = ActorUtil.executeRemoteOperation(this.context().actorSelection(remoteActorPath),
- executeMsg, ActorUtil.REMOTE_ASK_DURATION, ActorUtil.REMOTE_AWAIT_DURATION);
-
- getSender().tell(operationRes, self());
- }
- } catch (Exception e) {
- LOG.error(e.toString());
- getSender().tell(new ErrorResponse(e), self());
- }
- }
-
private void invokeRemoteRpc(InvokeRpc msg) {
// Look up the remote actor to execute rpc
LOG.debug("Looking up the remote actor for route {}", msg);
try {
- RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, msg.getRpc(), null);
- GetRpc rpcMsg = new GetRpc(routeId);
- GetRpcReply rpcReply = (GetRpcReply)ActorUtil.executeLocalOperation(rpcRegistry, rpcMsg, ActorUtil.LOCAL_ASK_DURATION, ActorUtil.LOCAL_AWAIT_DURATION);
- String remoteActorPath = rpcReply.getRoutePath();
+ // Find router
+ RpcRouter.RouteIdentifier<?,?,?> routeId = new RouteIdentifierImpl(null, msg.getRpc(), msg.getIdentifier());
+ RpcRegistry.Messages.FindRouters rpcMsg = new RpcRegistry.Messages.FindRouters(routeId);
+ RpcRegistry.Messages.FindRoutersReply rpcReply =
+ (RpcRegistry.Messages.FindRoutersReply) ActorUtil.executeOperation(rpcRegistry, rpcMsg, ActorUtil.LOCAL_ASK_DURATION, ActorUtil.LOCAL_AWAIT_DURATION);
+
+ List<Pair<ActorRef, Long>> actorRefList = rpcReply.getRouterWithUpdateTime();
- if(remoteActorPath == null) {
+ if(actorRefList == null || actorRefList.isEmpty()) {
LOG.debug("No remote actor found for rpc {{}}.", msg.getRpc());
getSender().tell(new ErrorResponse(
- new IllegalStateException("No remote actor found for rpc execution of : " + msg.getRpc())), self());
+ new IllegalStateException("No remote actor found for rpc execution of : " + msg.getRpc())), self());
} else {
+ RoutingLogic logic = new LatestEntryRoutingLogic(actorRefList);
ExecuteRpc executeMsg = new ExecuteRpc(XmlUtils.inputCompositeNodeToXml(msg.getInput(), schemaContext), msg.getRpc());
- Object operationRes = ActorUtil.executeRemoteOperation(this.context().actorSelection(remoteActorPath),
+ Object operationRes = ActorUtil.executeOperation(logic.select(),
executeMsg, ActorUtil.REMOTE_ASK_DURATION, ActorUtil.REMOTE_AWAIT_DURATION);
getSender().tell(operationRes, self());
}
} catch (Exception e) {
- LOG.error(e.toString());
+ LOG.error("invokeRemoteRpc: {}", e);
getSender().tell(new ErrorResponse(e), self());
}
}
+
+
private void executeRpc(ExecuteRpc msg) {
LOG.debug("Executing rpc for rpc {}", msg.getRpc());
try {
- Future<RpcResult<CompositeNode>> rpc = brokerSession.rpc(msg.getRpc(), XmlUtils.inputXmlToCompositeNode(msg.getRpc(), msg.getInputCompositeNode(), schemaContext));
+ Future<RpcResult<CompositeNode>> rpc = brokerSession.rpc(msg.getRpc(),
+ XmlUtils.inputXmlToCompositeNode(msg.getRpc(), msg.getInputCompositeNode(), schemaContext));
RpcResult<CompositeNode> rpcResult = rpc != null ? rpc.get():null;
CompositeNode result = rpcResult != null ? rpcResult.getResult() : null;
getSender().tell(new RpcResponse(XmlUtils.outputCompositeNodeToXml(result, schemaContext)), self());
} catch (Exception e) {
- LOG.error(e.toString());
+ LOG.error("executeRpc: {}", e);
getSender().tell(new ErrorResponse(e), self());
}
}
import akka.actor.ActorRef;
-import org.opendaylight.controller.remote.rpc.messages.AddRpc;
-import org.opendaylight.controller.remote.rpc.messages.RemoveRpc;
-import org.opendaylight.controller.remote.rpc.utils.ActorUtil;
+import org.opendaylight.controller.remote.rpc.registry.RpcRegistry;
+import org.opendaylight.controller.sal.connector.api.RpcRouter;
import org.opendaylight.controller.sal.core.api.RpcRegistrationListener;
import org.opendaylight.yangtools.yang.common.QName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.ArrayList;
+import java.util.List;
+
public class RpcListener implements RpcRegistrationListener{
private static final Logger LOG = LoggerFactory.getLogger(RpcListener.class);
private final ActorRef rpcRegistry;
- private final String actorPath;
- public RpcListener(ActorRef rpcRegistry, String actorPath) {
+ public RpcListener(ActorRef rpcRegistry) {
this.rpcRegistry = rpcRegistry;
- this.actorPath = actorPath;
}
@Override
public void onRpcImplementationAdded(QName rpc) {
LOG.debug("Adding registration for [{}]", rpc);
- RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, rpc, null);
- AddRpc addRpcMsg = new AddRpc(routeId, actorPath);
- try {
- ActorUtil.executeLocalOperation(rpcRegistry, addRpcMsg, ActorUtil.LOCAL_ASK_DURATION, ActorUtil.LOCAL_AWAIT_DURATION);
- LOG.debug("Route added [{}-{}]", routeId, this.actorPath);
- } catch (Exception e) {
- // Just logging it because Akka API throws this exception
- LOG.error(e.toString());
- }
-
+ RpcRouter.RouteIdentifier<?,?,?> routeId = new RouteIdentifierImpl(null, rpc, null);
+ List<RpcRouter.RouteIdentifier<?,?,?>> routeIds = new ArrayList<>();
+ routeIds.add(routeId);
+ RpcRegistry.Messages.AddOrUpdateRoutes addRpcMsg = new RpcRegistry.Messages.AddOrUpdateRoutes(routeIds);
+ rpcRegistry.tell(addRpcMsg, ActorRef.noSender());
}
@Override
public void onRpcImplementationRemoved(QName rpc) {
LOG.debug("Removing registration for [{}]", rpc);
- RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, rpc, null);
- RemoveRpc removeRpcMsg = new RemoveRpc(routeId);
- try {
- ActorUtil.executeLocalOperation(rpcRegistry, removeRpcMsg, ActorUtil.LOCAL_ASK_DURATION, ActorUtil.LOCAL_AWAIT_DURATION);
- } catch (Exception e) {
- // Just logging it because Akka API throws this exception
- LOG.error(e.toString());
- }
+ RpcRouter.RouteIdentifier<?,?,?> routeId = new RouteIdentifierImpl(null, rpc, null);
+ List<RpcRouter.RouteIdentifier<?,?,?>> routeIds = new ArrayList<>();
+ routeIds.add(routeId);
+ RpcRegistry.Messages.RemoveRoutes removeRpcMsg = new RpcRegistry.Messages.RemoveRoutes(routeIds);
+ rpcRegistry.tell(removeRpcMsg, ActorRef.noSender());
}
}
import akka.japi.Creator;
import akka.japi.Function;
import org.opendaylight.controller.remote.rpc.messages.UpdateSchemaContext;
-import org.opendaylight.controller.remote.rpc.registry.ClusterWrapper;
-import org.opendaylight.controller.remote.rpc.registry.RpcRegistryOld;
+import org.opendaylight.controller.remote.rpc.registry.RpcRegistry;
import org.opendaylight.controller.sal.core.api.Broker;
import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
import org.opendaylight.yangtools.yang.common.QName;
private static final Logger LOG = LoggerFactory.getLogger(RpcManager.class);
private SchemaContext schemaContext;
- private final ClusterWrapper clusterWrapper;
private ActorRef rpcBroker;
private ActorRef rpcRegistry;
private final Broker.ProviderSession brokerSession;
private RemoteRpcImplementation rpcImplementation;
private final RpcProvisionRegistry rpcProvisionRegistry;
- private RpcManager(ClusterWrapper clusterWrapper, SchemaContext schemaContext,
+ private RpcManager(SchemaContext schemaContext,
Broker.ProviderSession brokerSession, RpcProvisionRegistry rpcProvisionRegistry) {
- this.clusterWrapper = clusterWrapper;
this.schemaContext = schemaContext;
this.brokerSession = brokerSession;
this.rpcProvisionRegistry = rpcProvisionRegistry;
}
- public static Props props(final ClusterWrapper clusterWrapper, final SchemaContext schemaContext,
+ public static Props props(final SchemaContext schemaContext,
final Broker.ProviderSession brokerSession, final RpcProvisionRegistry rpcProvisionRegistry) {
return Props.create(new Creator<RpcManager>() {
@Override
public RpcManager create() throws Exception {
- return new RpcManager(clusterWrapper, schemaContext, brokerSession, rpcProvisionRegistry);
+ return new RpcManager(schemaContext, brokerSession, rpcProvisionRegistry);
}
});
}
private void createRpcActors() {
LOG.debug("Create rpc registry and broker actors");
- rpcRegistry = getContext().actorOf(RpcRegistryOld.props(clusterWrapper), ActorConstants.RPC_REGISTRY);
+
+ rpcRegistry = getContext().actorOf(Props.create(RpcRegistry.class), ActorConstants.RPC_REGISTRY);
+
rpcBroker = getContext().actorOf(RpcBroker.props(brokerSession, rpcRegistry, schemaContext), ActorConstants.RPC_BROKER);
+ RpcRegistry.Messages.SetLocalRouter localRouter = new RpcRegistry.Messages.SetLocalRouter(rpcBroker);
+ rpcRegistry.tell(localRouter, self());
}
private void startListeners() {
LOG.debug("Registers rpc listeners");
- String rpcBrokerPath = clusterWrapper.getAddress().toString() + ActorConstants.RPC_BROKER_PATH;
- rpcListener = new RpcListener(rpcRegistry, rpcBrokerPath);
- routeChangeListener = new RoutedRpcListener(rpcRegistry, rpcBrokerPath);
+ rpcListener = new RpcListener(rpcRegistry);
+ routeChangeListener = new RoutedRpcListener(rpcRegistry);
rpcImplementation = new RemoteRpcImplementation(rpcBroker, schemaContext);
brokerSession.addRpcRegistrationListener(rpcListener);
+++ /dev/null
-/*
- * Copyright (c) 2014 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.remote.rpc.messages;
-
-import com.google.common.base.Preconditions;
-import org.opendaylight.controller.sal.connector.api.RpcRouter;
-
-import java.io.Serializable;
-import java.util.Set;
-
-public class AddRoutedRpc implements Serializable {
-
- private final Set<RpcRouter.RouteIdentifier<?, ?, ?>> announcements;
- private final String actorPath;
-
- public AddRoutedRpc(final Set<RpcRouter.RouteIdentifier<?, ?, ?>> announcements, final String actorPath) {
- Preconditions.checkNotNull(announcements, "Route identifier should not be null");
- Preconditions.checkNotNull(actorPath, "Actor path should not be null");
-
- this.announcements = announcements;
- this.actorPath = actorPath;
- }
-
- public Set<RpcRouter.RouteIdentifier<?, ?, ?>> getAnnouncements() {
- return announcements;
- }
-
- public String getActorPath() {
- return actorPath;
- }
-}
+++ /dev/null
-/*
- * Copyright (c) 2014 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.remote.rpc.messages;
-
-import com.google.common.base.Preconditions;
-import org.opendaylight.controller.remote.rpc.RouteIdentifierImpl;
-
-import java.io.Serializable;
-
-public class AddRpc implements Serializable {
-
- private final RouteIdentifierImpl routeId;
- private final String actorPath;
-
- public AddRpc(final RouteIdentifierImpl routeId, final String actorPath) {
- Preconditions.checkNotNull(routeId, "Route identifier should not be null");
- Preconditions.checkNotNull(actorPath, "Actor path should not be null");
-
- this.routeId = routeId;
- this.actorPath = actorPath;
- }
-
- public RouteIdentifierImpl getRouteId() {
- return routeId;
- }
-
- public String getActorPath() {
- return actorPath;
- }
-}
\ No newline at end of file
+++ /dev/null
-/*
- * Copyright (c) 2014 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.remote.rpc.messages;
-
-
-import com.google.common.base.Preconditions;
-import org.opendaylight.controller.remote.rpc.RouteIdentifierImpl;
-
-import java.io.Serializable;
-
-public class GetRoutedRpc implements Serializable {
-
- private final RouteIdentifierImpl routeId;
-
- public GetRoutedRpc(final RouteIdentifierImpl routeId) {
- Preconditions.checkNotNull(routeId, "route id should not be null");
- this.routeId = routeId;
- }
-
- public RouteIdentifierImpl getRouteId() {
- return routeId;
- }
-}
+++ /dev/null
-package org.opendaylight.controller.remote.rpc.messages;
-
-/*
- * Copyright (c) 2014 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
- */
-
-import java.io.Serializable;
-
-public class GetRoutedRpcReply implements Serializable {
-
- private final String routePath;
-
- public GetRoutedRpcReply(final String routePath) {
- this.routePath = routePath;
- }
-
- public String getRoutePath() {
- return routePath;
- }
-}
+++ /dev/null
-/*
- * Copyright (c) 2014 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.remote.rpc.messages;
-
-import com.google.common.base.Preconditions;
-import org.opendaylight.controller.remote.rpc.RouteIdentifierImpl;
-
-import java.io.Serializable;
-
-public class GetRpc implements Serializable {
-
- private final RouteIdentifierImpl routeId;
-
- public GetRpc(final RouteIdentifierImpl routeId) {
- Preconditions.checkNotNull(routeId, "Route Id should not be null");
- this.routeId = routeId;
- }
-
- public RouteIdentifierImpl getRouteId() {
- return routeId;
- }
-}
+++ /dev/null
-package org.opendaylight.controller.remote.rpc.messages;
-
-/*
- * Copyright (c) 2014 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
- */
-
-import java.io.Serializable;
-
-public class GetRpcReply implements Serializable {
-
- private final String routePath;
-
- public GetRpcReply(final String routePath) {
- this.routePath = routePath;
- }
-
- public String getRoutePath() {
- return routePath;
- }
-}
+++ /dev/null
-/*
- * Copyright (c) 2014 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.remote.rpc.messages;
-
-import com.google.common.base.Preconditions;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.CompositeNode;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-
-import java.io.Serializable;
-
-public class InvokeRoutedRpc implements Serializable {
-
- private final QName rpc;
- private final YangInstanceIdentifier identifier;
- private final CompositeNode input;
-
- public InvokeRoutedRpc(final QName rpc, final YangInstanceIdentifier identifier, final CompositeNode input) {
- Preconditions.checkNotNull(rpc, "rpc qname should not be null");
- Preconditions.checkNotNull(identifier, "instance identifier of routed rpc should not be null");
- Preconditions.checkNotNull(input, "rpc input should not be null");
-
- this.rpc = rpc;
- this.identifier = identifier;
- this.input = input;
- }
-
- public QName getRpc() {
- return rpc;
- }
-
- public YangInstanceIdentifier getIdentifier() {
- return identifier;
- }
-
- public CompositeNode getInput() {
- return input;
- }
-}
import com.google.common.base.Preconditions;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import java.io.Serializable;
public class InvokeRpc implements Serializable {
private final QName rpc;
+ private final YangInstanceIdentifier identifier;
private final CompositeNode input;
- public InvokeRpc(final QName rpc, final CompositeNode input) {
+ public InvokeRpc(final QName rpc, final YangInstanceIdentifier identifier, final CompositeNode input) {
Preconditions.checkNotNull(rpc, "rpc qname should not be null");
Preconditions.checkNotNull(input, "rpc input should not be null");
this.rpc = rpc;
+ this.identifier = identifier;
this.input = input;
}
return rpc;
}
+ public YangInstanceIdentifier getIdentifier() {
+ return identifier;
+ }
+
public CompositeNode getInput() {
return input;
}
+++ /dev/null
-/*
- * Copyright (c) 2014 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.remote.rpc.messages;
-
-import com.google.common.base.Preconditions;
-import org.opendaylight.controller.sal.connector.api.RpcRouter;
-
-import java.io.Serializable;
-import java.util.Set;
-
-public class RemoveRoutedRpc implements Serializable {
-
- private final Set<RpcRouter.RouteIdentifier<?, ?, ?>> announcements;
- private final String actorPath;
-
- public RemoveRoutedRpc(final Set<RpcRouter.RouteIdentifier<?, ?, ?>> announcements, final String actorPath) {
- Preconditions.checkNotNull(announcements, "Route identifier should not be null");
- Preconditions.checkNotNull(actorPath, "Actor path should not be null");
-
- this.announcements = announcements;
- this.actorPath = actorPath;
- }
-
- public Set<RpcRouter.RouteIdentifier<?, ?, ?>> getAnnouncements() {
- return announcements;
- }
-
- public String getActorPath() {
- return actorPath;
- }
-}
+++ /dev/null
-/*
- * Copyright (c) 2014 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.remote.rpc.messages;
-
-import com.google.common.base.Preconditions;
-import org.opendaylight.controller.remote.rpc.RouteIdentifierImpl;
-
-import java.io.Serializable;
-
-public class RemoveRpc implements Serializable {
-
- private final RouteIdentifierImpl routeId;
-
- public RemoveRpc(final RouteIdentifierImpl routeId) {
- Preconditions.checkNotNull(routeId, "Route Id should not be null");
-
- this.routeId = routeId;
- }
-
- public RouteIdentifierImpl getRouteId() {
- return routeId;
- }
-}
\ No newline at end of file
+++ /dev/null
-/*
- * Copyright (c) 2014 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.remote.rpc.messages;
-
-import org.opendaylight.controller.sal.connector.api.RpcRouter;
-
-import java.io.Serializable;
-import java.util.LinkedHashSet;
-import java.util.Map;
-
-public class RoutingTableData implements Serializable {
- private final Map<RpcRouter.RouteIdentifier<?, ?, ?>, String> rpcMap;
- private final Map<RpcRouter.RouteIdentifier<?, ?, ?>, LinkedHashSet<String>> routedRpcMap;
-
- public RoutingTableData(final Map<RpcRouter.RouteIdentifier<?, ?, ?>, String> rpcMap,
- final Map<RpcRouter.RouteIdentifier<?, ?, ?>, LinkedHashSet<String>> routedRpcMap) {
- this.rpcMap = rpcMap;
- this.routedRpcMap = routedRpcMap;
- }
-
- public Map<RpcRouter.RouteIdentifier<?, ?, ?>, String> getRpcMap() {
- return rpcMap;
- }
-
- public Map<RpcRouter.RouteIdentifier<?, ?, ?>, LinkedHashSet<String>> getRoutedRpcMap() {
- return routedRpcMap;
- }
-}
+++ /dev/null
-/*
- * Copyright (c) 2014 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.remote.rpc.registry;
-
-
-import akka.actor.Address;
-import akka.cluster.ClusterEvent;
-
-public interface ClusterWrapper {
-
- ClusterEvent.CurrentClusterState getState();
-
- Address getAddress();
-}
+++ /dev/null
-/*
- * Copyright (c) 2014 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.remote.rpc.registry;
-
-
-import akka.actor.ActorSystem;
-import akka.actor.Address;
-import akka.cluster.Cluster;
-import akka.cluster.ClusterEvent;
-
-
-public class ClusterWrapperImpl implements ClusterWrapper{
-
- private Cluster cluster;
-
- public ClusterWrapperImpl(ActorSystem actorSystem) {
- cluster = Cluster.get(actorSystem);
- }
-
- @Override
- public ClusterEvent.CurrentClusterState getState() {
- return cluster.state();
- }
-
- @Override
- public Address getAddress() {
- return cluster.selfAddress();
- }
-}
+++ /dev/null
-/*
- * Copyright (c) 2014 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.remote.rpc.registry;
-
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableSet;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.LinkedHashSet;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-
-public class RoutingTableOld<I, R> {
-
- private final Logger LOG = LoggerFactory.getLogger(RoutingTableOld.class);
-
- private ConcurrentMap<I,R> globalRpcMap = new ConcurrentHashMap<>();
- private ConcurrentMap<I, LinkedHashSet<R>> routedRpcMap = new ConcurrentHashMap<>();
-
- public ConcurrentMap<I, R> getGlobalRpcMap() {
- return globalRpcMap;
- }
-
- public ConcurrentMap<I, LinkedHashSet<R>> getRoutedRpcMap() {
- return routedRpcMap;
- }
-
- public R getGlobalRoute(final I routeId) {
- Preconditions.checkNotNull(routeId, "getGlobalRoute: routeId cannot be null!");
- return globalRpcMap.get(routeId);
- }
-
- public void addGlobalRoute(final I routeId, final R route) {
- Preconditions.checkNotNull(routeId, "addGlobalRoute: routeId cannot be null!");
- Preconditions.checkNotNull(route, "addGlobalRoute: route cannot be null!");
- LOG.debug("addGlobalRoute: adding a new route with id[{}] and value [{}]", routeId, route);
- if(globalRpcMap.putIfAbsent(routeId, route) != null) {
- LOG.debug("A route already exist for route id [{}] ", routeId);
- }
- }
-
- public void removeGlobalRoute(final I routeId) {
- Preconditions.checkNotNull(routeId, "removeGlobalRoute: routeId cannot be null!");
- LOG.debug("removeGlobalRoute: removing a new route with id [{}]", routeId);
- globalRpcMap.remove(routeId);
- }
-
- public Set<R> getRoutedRpc(final I routeId) {
- Preconditions.checkNotNull(routeId, "getRoutes: routeId cannot be null!");
- Set<R> routes = routedRpcMap.get(routeId);
-
- if (routes == null) {
- return Collections.emptySet();
- }
-
- return ImmutableSet.copyOf(routes);
- }
-
- public R getLastAddedRoutedRpc(final I routeId) {
-
- Set<R> routes = getRoutedRpc(routeId);
-
- if (routes.isEmpty()) {
- return null;
- }
-
- R route = null;
- Iterator<R> iter = routes.iterator();
- while (iter.hasNext()) {
- route = iter.next();
- }
-
- return route;
- }
-
- public void addRoutedRpc(final I routeId, final R route) {
- Preconditions.checkNotNull(routeId, "addRoute: routeId cannot be null");
- Preconditions.checkNotNull(route, "addRoute: route cannot be null");
- LOG.debug("addRoute: adding a route with k/v [{}/{}]", routeId, route);
- threadSafeAdd(routeId, route);
- }
-
- public void addRoutedRpcs(final Set<I> routeIds, final R route) {
- Preconditions.checkNotNull(routeIds, "addRoutes: routeIds must not be null");
- for (I routeId : routeIds){
- addRoutedRpc(routeId, route);
- }
- }
-
- public void removeRoute(final I routeId, final R route) {
- Preconditions.checkNotNull(routeId, "removeRoute: routeId cannot be null!");
- Preconditions.checkNotNull(route, "removeRoute: route cannot be null!");
-
- LinkedHashSet<R> routes = routedRpcMap.get(routeId);
- if (routes == null) {
- return;
- }
- LOG.debug("removeRoute: removing a new route with k/v [{}/{}]", routeId, route);
- threadSafeRemove(routeId, route);
- }
-
- public void removeRoutes(final Set<I> routeIds, final R route) {
- Preconditions.checkNotNull(routeIds, "removeRoutes: routeIds must not be null");
- for (I routeId : routeIds){
- removeRoute(routeId, route);
- }
- }
-
- /**
- * This method guarantees that no 2 thread over write each other's changes.
- * Just so that we dont end up in infinite loop, it tries for 100 times then throw
- */
- private void threadSafeAdd(final I routeId, final R route) {
-
- for (int i=0;i<100;i++){
-
- LinkedHashSet<R> updatedRoutes = new LinkedHashSet<>();
- updatedRoutes.add(route);
- LinkedHashSet<R> oldRoutes = routedRpcMap.putIfAbsent(routeId, updatedRoutes);
- if (oldRoutes == null) {
- return;
- }
-
- updatedRoutes = new LinkedHashSet<>(oldRoutes);
- updatedRoutes.add(route);
-
- if (routedRpcMap.replace(routeId, oldRoutes, updatedRoutes)) {
- return;
- }
- }
- //the method did not already return means it failed to add route in 100 attempts
- throw new IllegalStateException("Failed to add route [" + routeId + "]");
- }
-
- /**
- * This method guarantees that no 2 thread over write each other's changes.
- * Just so that we dont end up in infinite loop, it tries for 100 times then throw
- */
- private void threadSafeRemove(final I routeId, final R route) {
- LinkedHashSet<R> updatedRoutes = null;
- for (int i=0;i<100;i++){
- LinkedHashSet<R> oldRoutes = routedRpcMap.get(routeId);
-
- // if route to be deleted is the only entry in the set then remove routeId from the cache
- if ((oldRoutes.size() == 1) && oldRoutes.contains(route)){
- routedRpcMap.remove(routeId);
- return;
- }
-
- // if there are multiple routes for this routeId, remove the route to be deleted only from the set.
- updatedRoutes = new LinkedHashSet<>(oldRoutes);
- updatedRoutes.remove(route);
- if (routedRpcMap.replace(routeId, oldRoutes, updatedRoutes)) {
- return;
- }
-
- }
- //the method did not already return means it failed to remove route in 100 attempts
- throw new IllegalStateException("Failed to remove route [" + routeId + "]");
- }
-}
+++ /dev/null
-/*
- * Copyright (c) 2014 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.remote.rpc.registry;
-
-import akka.actor.ActorSelection;
-import akka.actor.Address;
-import akka.actor.Props;
-import akka.cluster.ClusterEvent;
-import akka.cluster.Member;
-import akka.japi.Creator;
-import org.opendaylight.controller.remote.rpc.AbstractUntypedActor;
-import org.opendaylight.controller.remote.rpc.ActorConstants;
-import org.opendaylight.controller.remote.rpc.messages.AddRoutedRpc;
-import org.opendaylight.controller.remote.rpc.messages.AddRpc;
-import org.opendaylight.controller.remote.rpc.messages.GetRoutedRpc;
-import org.opendaylight.controller.remote.rpc.messages.GetRoutedRpcReply;
-import org.opendaylight.controller.remote.rpc.messages.GetRpc;
-import org.opendaylight.controller.remote.rpc.messages.GetRpcReply;
-import org.opendaylight.controller.remote.rpc.messages.RemoveRoutedRpc;
-import org.opendaylight.controller.remote.rpc.messages.RemoveRpc;
-import org.opendaylight.controller.remote.rpc.messages.RoutingTableData;
-import org.opendaylight.controller.sal.connector.api.RpcRouter;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import scala.collection.JavaConversions;
-
-import java.util.LinkedHashSet;
-import java.util.Map;
-import java.util.Random;
-import java.util.Set;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-
-/**
- * This Actor maintains the routing table state and sync it with other nodes in the cluster.
- *
- * A scheduler runs after an interval of time, which pick a random member from the cluster
- * and send the current state of routing table to the member.
- *
- * when a message of routing table data is received, it gets merged with the local routing table
- * to keep the latest data.
- */
-
-public class RpcRegistryOld extends AbstractUntypedActor {
-
- private static final Logger LOG = LoggerFactory.getLogger(RpcRegistryOld.class);
- private RoutingTableOld<RpcRouter.RouteIdentifier<?, ?, ?>, String> routingTable;
- private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
- private final ClusterWrapper clusterWrapper;
- private final ScheduledFuture<?> syncScheduler;
-
- private RpcRegistryOld(ClusterWrapper clusterWrapper){
- this.routingTable = new RoutingTableOld<>();
- this.clusterWrapper = clusterWrapper;
- this.syncScheduler = scheduler.scheduleAtFixedRate(new SendRoutingTable(), 10, 10, TimeUnit.SECONDS);
- }
-
- public static Props props(final ClusterWrapper clusterWrapper){
- return Props.create(new Creator<RpcRegistryOld>(){
-
- @Override
- public RpcRegistryOld create() throws Exception {
- return new RpcRegistryOld(clusterWrapper);
- }
- });
- }
-
- @Override
- protected void handleReceive(Object message) throws Exception {
- LOG.debug("Received message {}", message);
- if(message instanceof RoutingTableData) {
- syncRoutingTable((RoutingTableData) message);
- } else if(message instanceof GetRoutedRpc) {
- getRoutedRpc((GetRoutedRpc) message);
- } else if(message instanceof GetRpc) {
- getRpc((GetRpc) message);
- } else if(message instanceof AddRpc) {
- addRpc((AddRpc) message);
- } else if(message instanceof RemoveRpc) {
- removeRpc((RemoveRpc) message);
- } else if(message instanceof AddRoutedRpc) {
- addRoutedRpc((AddRoutedRpc) message);
- } else if(message instanceof RemoveRoutedRpc) {
- removeRoutedRpc((RemoveRoutedRpc) message);
- }
- }
-
- private void getRoutedRpc(GetRoutedRpc rpcMsg){
- LOG.debug("Get latest routed Rpc location from routing table {}", rpcMsg);
- String remoteActorPath = routingTable.getLastAddedRoutedRpc(rpcMsg.getRouteId());
- GetRoutedRpcReply routedRpcReply = new GetRoutedRpcReply(remoteActorPath);
-
- getSender().tell(routedRpcReply, self());
- }
-
- private void getRpc(GetRpc rpcMsg) {
- LOG.debug("Get global Rpc location from routing table {}", rpcMsg);
- String remoteActorPath = routingTable.getGlobalRoute(rpcMsg.getRouteId());
- GetRpcReply rpcReply = new GetRpcReply(remoteActorPath);
-
- getSender().tell(rpcReply, self());
- }
-
- private void addRpc(AddRpc rpcMsg) {
- LOG.debug("Add Rpc to routing table {}", rpcMsg);
- routingTable.addGlobalRoute(rpcMsg.getRouteId(), rpcMsg.getActorPath());
-
- getSender().tell("Success", self());
- }
-
- private void removeRpc(RemoveRpc rpcMsg) {
- LOG.debug("Removing Rpc to routing table {}", rpcMsg);
- routingTable.removeGlobalRoute(rpcMsg.getRouteId());
-
- getSender().tell("Success", self());
- }
-
- private void addRoutedRpc(AddRoutedRpc rpcMsg) {
- routingTable.addRoutedRpcs(rpcMsg.getAnnouncements(), rpcMsg.getActorPath());
- getSender().tell("Success", self());
- }
-
- private void removeRoutedRpc(RemoveRoutedRpc rpcMsg) {
- routingTable.removeRoutes(rpcMsg.getAnnouncements(), rpcMsg.getActorPath());
- getSender().tell("Success", self());
- }
-
- private void syncRoutingTable(RoutingTableData routingTableData) {
- LOG.debug("Syncing routing table {}", routingTableData);
-
- Map<RpcRouter.RouteIdentifier<?, ?, ?>, String> newRpcMap = routingTableData.getRpcMap();
- Set<RpcRouter.RouteIdentifier<?, ?, ?>> routeIds = newRpcMap.keySet();
- for(RpcRouter.RouteIdentifier<?, ?, ?> routeId : routeIds) {
- routingTable.addGlobalRoute(routeId, newRpcMap.get(routeId));
- }
-
- Map<RpcRouter.RouteIdentifier<?, ?, ?>, LinkedHashSet<String>> newRoutedRpcMap =
- routingTableData.getRoutedRpcMap();
- routeIds = newRoutedRpcMap.keySet();
-
- for(RpcRouter.RouteIdentifier<?, ?, ?> routeId : routeIds) {
- Set<String> routeAddresses = newRoutedRpcMap.get(routeId);
- for(String routeAddress : routeAddresses) {
- routingTable.addRoutedRpc(routeId, routeAddress);
- }
- }
- }
-
- private ActorSelection getRandomRegistryActor() {
- ClusterEvent.CurrentClusterState clusterState = clusterWrapper.getState();
- ActorSelection actor = null;
- Set<Member> members = JavaConversions.asJavaSet(clusterState.members());
- int memberSize = members.size();
- // Don't select yourself
- if(memberSize > 1) {
- Address currentNodeAddress = clusterWrapper.getAddress();
- int index = new Random().nextInt(memberSize);
- int i = 0;
- // keeping previous member, in case when random index member is same as current actor
- // and current actor member is last in set
- Member previousMember = null;
- for(Member member : members){
- if(i == index-1) {
- previousMember = member;
- }
- if(i == index) {
- if(!currentNodeAddress.equals(member.address())) {
- actor = this.context().actorSelection(member.address() + ActorConstants.RPC_REGISTRY_PATH);
- break;
- } else if(index < memberSize-1){ // pick the next element in the set
- index++;
- }
- }
- i++;
- }
- if(actor == null && previousMember != null) {
- actor = this.context().actorSelection(previousMember.address() + ActorConstants.RPC_REGISTRY_PATH);
- }
- }
- return actor;
- }
-
- private class SendRoutingTable implements Runnable {
-
- @Override
- public void run() {
- RoutingTableData routingTableData =
- new RoutingTableData(routingTable.getGlobalRpcMap(), routingTable.getRoutedRpcMap());
- LOG.debug("Sending routing table for sync {}", routingTableData);
- ActorSelection actor = getRandomRegistryActor();
- if(actor != null) {
- actor.tell(routingTableData, self());
- }
- }
- }
-}
package org.opendaylight.controller.remote.rpc.utils;
import akka.actor.ActorRef;
-import akka.actor.ActorSelection;
import akka.util.Timeout;
import scala.concurrent.Await;
import scala.concurrent.Future;
* @param awaitDuration
* @return The response of the operation
*/
- public static Object executeLocalOperation(ActorRef actor, Object message,
- FiniteDuration askDuration, FiniteDuration awaitDuration) throws Exception{
+ public static Object executeOperation(ActorRef actor, Object message,
+ FiniteDuration askDuration, FiniteDuration awaitDuration) throws Exception{
Future<Object> future =
ask(actor, message, new Timeout(askDuration));
return Await.result(future, awaitDuration);
}
- /**
- * Execute an operation on a remote actor and wait for it's response
- * @param actor
- * @param message
- * @param askDuration
- * @param awaitDuration
- * @return
- */
- public static Object executeRemoteOperation(ActorSelection actor, Object message,
- FiniteDuration askDuration, FiniteDuration awaitDuration) throws Exception{
- Future<Object> future =
- ask(actor, message, new Timeout(askDuration));
- return Await.result(future, awaitDuration);
- }
}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.remote.rpc.utils;
+
+import akka.actor.ActorRef;
+import akka.japi.Pair;
+import com.google.common.base.Preconditions;
+
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+/**
+ * This class will return First Entry
+ */
+public class LatestEntryRoutingLogic implements RoutingLogic{
+
+ private SortedSet<Pair<ActorRef, Long>> actorRefSet;
+
+ public LatestEntryRoutingLogic(Collection<Pair<ActorRef, Long>> entries) {
+ Preconditions.checkNotNull(entries, "Entries should not be null");
+ Preconditions.checkArgument(!entries.isEmpty(), "Entries collection should not be empty");
+
+ actorRefSet = new TreeSet<>(new LatestEntryComparator());
+ actorRefSet.addAll(entries);
+ }
+
+ @Override
+ public ActorRef select() {
+ return actorRefSet.last().first();
+ }
+
+
+ private class LatestEntryComparator implements Comparator<Pair<ActorRef, Long>> {
+
+ @Override
+ public int compare(Pair<ActorRef, Long> o1, Pair<ActorRef, Long> o2) {
+ if(o1 == null && o2 == null) {
+ return 0;
+ }
+ if(o1 == null && o2 != null) {
+ return -1;
+ }
+ if(o1 != null && o2 == null) {
+ return 1;
+ }
+
+ return o1.second().compareTo(o2.second());
+
+ }
+
+ }
+}
+
+
--- /dev/null
+/*
+ * Copyright (c) 2014 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.remote.rpc.utils;
+
+import akka.actor.ActorRef;
+
+/**
+ * This Interface is added to abstract out the way rpc execution could be
+ * routed, if more than one node in cluster is capable of executing the rpc.
+ *
+ * We can pick node randomly, round robin manner or based on last updated time etc.
+ */
+
+public interface RoutingLogic {
+
+ ActorRef select();
+}
cluster {
seed-nodes = ["akka.tcp://opendaylight-cluster-rpc@127.0.0.1:2551"]
-
auto-down-unreachable-after = 10s
}
}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.remote.rpc;
+
+
+import akka.actor.ActorSystem;
+import junit.framework.Assert;
+import org.junit.After;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ActorSystemFactoryTest {
+ ActorSystem system = null;
+
+ @Test
+ public void testActorSystemCreation(){
+ BundleContext context = mock(BundleContext.class);
+ when(context.getBundle()).thenReturn(mock(Bundle.class));
+ ActorSystemFactory.createInstance(context);
+ system = ActorSystemFactory.getInstance();
+ Assert.assertNotNull(system);
+ // Check illegal state exception
+
+ try {
+ ActorSystemFactory.createInstance(context);
+ fail("Illegal State exception should be thrown, while creating actor system second time");
+ } catch (IllegalStateException e) {
+ }
+ }
+
+ @After
+ public void cleanup() throws InterruptedException {
+ if(system != null) {
+ system.shutdown();
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.remote.rpc;
+
+
+import akka.actor.ActorRef;
+import akka.actor.ActorSystem;
+import akka.testkit.JavaTestKit;
+import com.typesafe.config.ConfigFactory;
+import junit.framework.Assert;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.controller.sal.core.api.Broker;
+import org.opendaylight.controller.sal.core.api.RpcProvisionRegistry;
+import org.opendaylight.controller.sal.core.api.model.SchemaService;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import scala.concurrent.Await;
+import scala.concurrent.duration.Duration;
+
+
+import java.util.concurrent.TimeUnit;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class RemoteRpcProviderTest {
+
+ static ActorSystem system;
+
+
+ @BeforeClass
+ public static void setup() throws InterruptedException {
+ system = ActorSystem.create("opendaylight-rpc", ConfigFactory.load().getConfig("odl-cluster"));
+ }
+
+ @AfterClass
+ public static void teardown() {
+ JavaTestKit.shutdownActorSystem(system);
+ system = null;
+ }
+
+ @Test
+ public void testRemoteRpcProvider() throws Exception {
+ RemoteRpcProvider rpcProvider = new RemoteRpcProvider(system, mock(RpcProvisionRegistry.class));
+ Broker.ProviderSession session = mock(Broker.ProviderSession.class);
+ SchemaService schemaService = mock(SchemaService.class);
+ when(schemaService.getGlobalContext()). thenReturn(mock(SchemaContext.class));
+ when(session.getService(SchemaService.class)).thenReturn(schemaService);
+ rpcProvider.onSessionInitiated(session);
+ ActorRef actorRef = Await.result(system.actorSelection(ActorConstants.RPC_MANAGER_PATH).resolveOne(Duration.create(1, TimeUnit.SECONDS)),
+ Duration.create(2, TimeUnit.SECONDS));
+ Assert.assertTrue(actorRef.path().toString().contains(ActorConstants.RPC_MANAGER_PATH));
+ }
+
+
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.remote.rpc;
+
+
+import akka.actor.ActorRef;
+import akka.actor.ActorSystem;
+import akka.testkit.JavaTestKit;
+import com.typesafe.config.ConfigFactory;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.common.impl.routing.RoutingUtils;
+import org.opendaylight.controller.remote.rpc.registry.RpcRegistry;
+import org.opendaylight.controller.sal.core.api.RpcRoutingContext;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+public class RouteRpcListenerTest {
+
+ static ActorSystem system;
+
+
+ @BeforeClass
+ public static void setup() throws InterruptedException {
+ system = ActorSystem.create("opendaylight-rpc", ConfigFactory.load().getConfig("odl-cluster"));
+ }
+
+ @AfterClass
+ public static void teardown() {
+ JavaTestKit.shutdownActorSystem(system);
+ system = null;
+ }
+
+ @Test
+ public void testRouteAdd() throws URISyntaxException, InterruptedException {
+ new JavaTestKit(system) {
+ {
+ // Test announcements
+ JavaTestKit probeReg = new JavaTestKit(system);
+ ActorRef rpcRegistry = probeReg.getRef();
+
+ RoutedRpcListener rpcListener = new RoutedRpcListener(rpcRegistry);
+
+ QName qName = new QName(new URI("actor2"), "actor2");
+ RpcRoutingContext context = RpcRoutingContext.create(qName, qName);
+ YangInstanceIdentifier identifier = YangInstanceIdentifier.create(new YangInstanceIdentifier.NodeIdentifier(qName));
+ rpcListener.onRouteChange(RoutingUtils.announcementChange(context, identifier));
+
+ probeReg.expectMsgClass(RpcRegistry.Messages.AddOrUpdateRoutes.class);
+ }};
+ }
+
+ @Test
+ public void testRouteRemove() throws URISyntaxException, InterruptedException {
+ new JavaTestKit(system) {
+ {
+ // Test announcements
+ JavaTestKit probeReg = new JavaTestKit(system);
+ ActorRef rpcRegistry = probeReg.getRef();
+
+ RoutedRpcListener rpcListener = new RoutedRpcListener(rpcRegistry);
+
+ QName qName = new QName(new URI("actor2"), "actor2");
+ RpcRoutingContext context = RpcRoutingContext.create(qName, qName);
+ YangInstanceIdentifier identifier = YangInstanceIdentifier.create(new YangInstanceIdentifier.NodeIdentifier(qName));
+ rpcListener.onRouteChange(RoutingUtils.removalChange(context, identifier));
+
+ probeReg.expectMsgClass(RpcRegistry.Messages.RemoveRoutes.class);
+ }};
+ }
+}
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
+import akka.japi.Pair;
import akka.testkit.JavaTestKit;
import com.google.common.util.concurrent.Futures;
+import com.typesafe.config.ConfigFactory;
import junit.framework.Assert;
import org.junit.AfterClass;
+import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.Mockito;
-import org.opendaylight.controller.remote.rpc.messages.AddRoutedRpc;
-import org.opendaylight.controller.remote.rpc.messages.AddRpc;
import org.opendaylight.controller.remote.rpc.messages.ErrorResponse;
-import org.opendaylight.controller.remote.rpc.messages.InvokeRoutedRpc;
import org.opendaylight.controller.remote.rpc.messages.InvokeRpc;
import org.opendaylight.controller.remote.rpc.messages.RpcResponse;
-import org.opendaylight.controller.remote.rpc.registry.ClusterWrapper;
-import org.opendaylight.controller.remote.rpc.registry.RpcRegistryOld;
+import org.opendaylight.controller.remote.rpc.registry.RpcRegistry;
import org.opendaylight.controller.sal.common.util.Rpcs;
-import org.opendaylight.controller.sal.connector.api.RpcRouter;
import org.opendaylight.controller.sal.core.api.Broker;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.RpcError;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
import org.opendaylight.yangtools.yang.data.api.ModifyAction;
import org.opendaylight.yangtools.yang.data.api.Node;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.List;
import java.util.concurrent.Future;
import static org.mockito.Mockito.mock;
public class RpcBrokerTest {
- static ActorSystem system;
+ static ActorSystem node1;
+ static ActorSystem node2;
+ private ActorRef rpcBroker1;
+ private JavaTestKit probeReg1;
+ private ActorRef rpcBroker2;
+ private JavaTestKit probeReg2;
+ private Broker.ProviderSession brokerSession;
@BeforeClass
- public static void setup() {
- system = ActorSystem.create();
+ public static void setup() throws InterruptedException {
+ node1 = ActorSystem.create("opendaylight-rpc", ConfigFactory.load().getConfig("memberA"));
+ node2 = ActorSystem.create("opendaylight-rpc", ConfigFactory.load().getConfig("memberB"));
}
@AfterClass
public static void teardown() {
- JavaTestKit.shutdownActorSystem(system);
- system = null;
+ JavaTestKit.shutdownActorSystem(node1);
+ JavaTestKit.shutdownActorSystem(node2);
+ node1 = null;
+ node2 = null;
}
+ @Before
+ public void createActor() {
+ brokerSession = Mockito.mock(Broker.ProviderSession.class);
+ SchemaContext schemaContext = mock(SchemaContext.class);
+ probeReg1 = new JavaTestKit(node1);
+ rpcBroker1 = node1.actorOf(RpcBroker.props(brokerSession, probeReg1.getRef(), schemaContext));
+ probeReg2 = new JavaTestKit(node2);
+ rpcBroker2 = node2.actorOf(RpcBroker.props(brokerSession, probeReg2.getRef(), schemaContext));
+
+ }
@Test
- public void testInvokeRpcError() throws URISyntaxException {
- new JavaTestKit(system) {{
- ActorRef rpcRegistry = system.actorOf(RpcRegistryOld.props(Mockito.mock(ClusterWrapper.class)));
- Broker.ProviderSession brokerSession = Mockito.mock(Broker.ProviderSession.class);
- SchemaContext schemaContext = mock(SchemaContext.class);
- ActorRef rpcBroker = system.actorOf(RpcBroker.props(brokerSession, rpcRegistry, schemaContext));
+ public void testInvokeRpcError() throws Exception {
+ new JavaTestKit(node1) {{
QName rpc = new QName(new URI("noactor1"), "noactor1");
CompositeNode input = new ImmutableCompositeNode(QName.create("ns", "2013-12-09", "no child"), new ArrayList<Node<?>>(), ModifyAction.REPLACE);
- InvokeRpc invokeMsg = new InvokeRpc(rpc, input);
- rpcBroker.tell(invokeMsg, getRef());
+
+
+ InvokeRpc invokeMsg = new InvokeRpc(rpc, null, input);
+ rpcBroker1.tell(invokeMsg, getRef());
+ probeReg1.expectMsgClass(RpcRegistry.Messages.FindRouters.class);
+ probeReg1.reply(new RpcRegistry.Messages.FindRoutersReply(new ArrayList<Pair<ActorRef, Long>>()));
Boolean getMsg = new ExpectMsg<Boolean>("ErrorResponse") {
protected Boolean match(Object in) {
}.get(); // this extracts the received message
Assert.assertTrue(getMsg);
+
}};
}
+
/**
* This test method invokes and executes the remote rpc
*/
@Test
public void testInvokeRpc() throws URISyntaxException {
- new JavaTestKit(system) {{
- ActorRef rpcRegistry = system.actorOf(RpcRegistryOld.props(mock(ClusterWrapper.class)));
- Broker.ProviderSession brokerSession = mock(Broker.ProviderSession.class);
- SchemaContext schemaContext = mock(SchemaContext.class);
- ActorRef rpcBroker = system.actorOf(RpcBroker.props(brokerSession, rpcRegistry, schemaContext));
- ActorRef rpcBrokerRemote = system.actorOf(RpcBroker.props(brokerSession, rpcRegistry, schemaContext), "actor1");
- // Add RPC in table
- QName rpc = new QName(new URI("actor1"), "actor1");
- RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, rpc, null);
- final String route = rpcBrokerRemote.path().toString();
- AddRpc rpcMsg = new AddRpc(routeId, route);
- rpcRegistry.tell(rpcMsg, getRef());
- expectMsgEquals(duration("2 second"), "Success");
-
+ new JavaTestKit(node1) {{
+ QName rpc = new QName(new URI("noactor1"), "noactor1");
// invoke rpc
CompositeNode input = new ImmutableCompositeNode(QName.create("ns", "2013-12-09", "child1"), new ArrayList<Node<?>>(), ModifyAction.REPLACE);
- CompositeNode invokeRpcResult = mock(CompositeNode.class);
- Collection<RpcError> errors = new ArrayList<>();
- RpcResult<CompositeNode> result = Rpcs.getRpcResult(true, invokeRpcResult, errors);
- Future<RpcResult<CompositeNode>> rpcResult = Futures.immediateFuture(result);
- when(brokerSession.rpc(rpc, input)).thenReturn(rpcResult);
- InvokeRpc invokeMsg = new InvokeRpc(rpc, input);
- rpcBroker.tell(invokeMsg, getRef());
-
- //verify response msg
- Boolean getMsg = new ExpectMsg<Boolean>("RpcResponse") {
- protected Boolean match(Object in) {
- if (in instanceof RpcResponse) {
- return true;
- } else {
- throw noMatch();
- }
- }
- }.get(); // this extracts the received message
-
- Assert.assertTrue(getMsg);
- }};
- }
-
- @Test
- public void testInvokeRoutedRpcError() throws URISyntaxException {
- new JavaTestKit(system) {{
- ActorRef rpcRegistry = system.actorOf(RpcRegistryOld.props(Mockito.mock(ClusterWrapper.class)));
- Broker.ProviderSession brokerSession = Mockito.mock(Broker.ProviderSession.class);
- SchemaContext schemaContext = mock(SchemaContext.class);
- ActorRef rpcBroker = system.actorOf(RpcBroker.props(brokerSession, rpcRegistry, schemaContext));
- QName rpc = new QName(new URI("actor1"), "actor1");
- CompositeNode input = new ImmutableCompositeNode(QName.create("ns", "2013-12-09", "child1"), new ArrayList<Node<?>>(), ModifyAction.REPLACE);
- InvokeRoutedRpc invokeMsg = new InvokeRoutedRpc(rpc, YangInstanceIdentifier.create(new YangInstanceIdentifier.NodeIdentifier(rpc)), input);
- rpcBroker.tell(invokeMsg, getRef());
+ InvokeRpc invokeMsg = new InvokeRpc(rpc, null, input);
+ rpcBroker1.tell(invokeMsg, getRef());
- Boolean getMsg = new ExpectMsg<Boolean>("ErrorResponse") {
- protected Boolean match(Object in) {
- if (in instanceof ErrorResponse) {
- ErrorResponse reply = (ErrorResponse)in;
- return "No remote actor found for rpc execution.".equals(reply.getException().getMessage());
- } else {
- throw noMatch();
- }
- }
- }.get(); // this extracts the received message
-
- Assert.assertTrue(getMsg);
- }};
- }
+ probeReg1.expectMsgClass(RpcRegistry.Messages.FindRouters.class);
+ List<Pair<ActorRef, Long>> routerList = new ArrayList<Pair<ActorRef, Long>>();
- /**
- * This test method invokes and executes the remote routed rpc
- */
+ routerList.add(new Pair<ActorRef, Long>(rpcBroker2, 200L));
- @Test
- public void testInvokeRoutedRpc() throws URISyntaxException {
- new JavaTestKit(system) {{
- ActorRef rpcRegistry = system.actorOf(RpcRegistryOld.props(mock(ClusterWrapper.class)));
- Broker.ProviderSession brokerSession = mock(Broker.ProviderSession.class);
- SchemaContext schemaContext = mock(SchemaContext.class);
- ActorRef rpcBroker = system.actorOf(RpcBroker.props(brokerSession, rpcRegistry, schemaContext));
- ActorRef rpcBrokerRemote = system.actorOf(RpcBroker.props(brokerSession, rpcRegistry, schemaContext), "actor2");
- // Add Routed RPC in table
- QName rpc = new QName(new URI("actor2"), "actor2");
- YangInstanceIdentifier identifier = YangInstanceIdentifier.create(new YangInstanceIdentifier.NodeIdentifier(rpc));
- RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, rpc, identifier);
- final String route = rpcBrokerRemote.path().toString();
- Set<RpcRouter.RouteIdentifier<?, ?, ?>> routeIds = new HashSet<>();
- routeIds.add(routeId);
-
- AddRoutedRpc rpcMsg = new AddRoutedRpc(routeIds, route);
- rpcRegistry.tell(rpcMsg, getRef());
- expectMsgEquals(duration("2 second"), "Success");
+ probeReg1.reply(new RpcRegistry.Messages.FindRoutersReply(routerList));
- // invoke rpc
- CompositeNode input = new ImmutableCompositeNode(QName.create("ns", "2013-12-09", "child1"), new ArrayList<Node<?>>(), ModifyAction.REPLACE);
CompositeNode invokeRpcResult = mock(CompositeNode.class);
Collection<RpcError> errors = new ArrayList<>();
RpcResult<CompositeNode> result = Rpcs.getRpcResult(true, invokeRpcResult, errors);
Future<RpcResult<CompositeNode>> rpcResult = Futures.immediateFuture(result);
when(brokerSession.rpc(rpc, input)).thenReturn(rpcResult);
- InvokeRoutedRpc invokeMsg = new InvokeRoutedRpc(rpc, identifier, input);
- rpcBroker.tell(invokeMsg, getRef());
//verify response msg
Boolean getMsg = new ExpectMsg<Boolean>("RpcResponse") {
Assert.assertTrue(getMsg);
}};
}
-
}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.remote.rpc;
+
+import akka.actor.ActorRef;
+import akka.actor.ActorSystem;
+import akka.testkit.JavaTestKit;
+import com.typesafe.config.ConfigFactory;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.controller.remote.rpc.registry.RpcRegistry;
+import org.opendaylight.yangtools.yang.common.QName;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+public class RpcListenerTest {
+
+ static ActorSystem system;
+
+
+ @BeforeClass
+ public static void setup() throws InterruptedException {
+ system = ActorSystem.create("opendaylight-rpc", ConfigFactory.load().getConfig("odl-cluster"));
+ }
+
+ @AfterClass
+ public static void teardown() {
+ JavaTestKit.shutdownActorSystem(system);
+ system = null;
+ }
+
+ @Test
+ public void testRpcAdd() throws URISyntaxException {
+ new JavaTestKit(system) {
+ {
+ JavaTestKit probeReg = new JavaTestKit(system);
+ ActorRef rpcRegistry = probeReg.getRef();
+
+ RpcListener rpcListener = new RpcListener(rpcRegistry);
+
+ QName qName = new QName(new URI("actor2"), "actor2");
+
+ rpcListener.onRpcImplementationAdded(qName);
+ probeReg.expectMsgClass(RpcRegistry.Messages.AddOrUpdateRoutes.class);
+ }};
+
+ }
+
+ @Test
+ public void testRpcRemove() throws URISyntaxException {
+ new JavaTestKit(system) {
+ {
+ JavaTestKit probeReg = new JavaTestKit(system);
+ ActorRef rpcRegistry = probeReg.getRef();
+
+ RpcListener rpcListener = new RpcListener(rpcRegistry);
+
+ QName qName = new QName(new URI("actor2"), "actor2");
+
+ rpcListener.onRpcImplementationRemoved(qName);
+ probeReg.expectMsgClass(RpcRegistry.Messages.RemoveRoutes.class);
+ }};
+
+ }
+}
+++ /dev/null
-/*
- * Copyright (c) 2014 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.remote.rpc.registry;
-
-import junit.framework.Assert;
-import org.junit.Test;
-import org.opendaylight.controller.remote.rpc.RouteIdentifierImpl;
-import org.opendaylight.controller.sal.connector.api.RpcRouter;
-import org.opendaylight.yangtools.yang.common.QName;
-
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.HashSet;
-import java.util.Set;
-
-public class RoutingTableOldTest {
-
- private RoutingTableOld<RpcRouter.RouteIdentifier<?, ?, ?>, String> routingTable =
- new RoutingTableOld<>();
-
- @Test
- public void addGlobalRouteNullRouteIdTest() {
- try {
- routingTable.addGlobalRoute(null, null);
-
- Assert.fail("Null pointer exception was not thrown.");
- } catch (Exception e) {
- Assert.assertEquals(NullPointerException.class.getName(), e.getClass().getName());
- Assert.assertEquals("addGlobalRoute: routeId cannot be null!", e.getMessage());
- }
- }
-
- @Test
- public void addGlobalRouteNullRouteTest() {
- try {
- QName type = new QName(new URI("actor1"), "actor1");
- RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, type, null);
- routingTable.addGlobalRoute(routeId, null);
-
- Assert.fail("Null pointer exception was not thrown.");
- } catch (Exception e) {
- Assert.assertEquals(NullPointerException.class.getName(), e.getClass().getName());
- Assert.assertEquals("addGlobalRoute: route cannot be null!", e.getMessage());
- }
- }
-
- @Test
- public void getGlobalRouteNullTest() {
- try {
- routingTable.getGlobalRoute(null);
-
- Assert.fail("Null pointer exception was not thrown.");
- } catch (Exception e) {
- Assert.assertEquals(NullPointerException.class.getName(), e.getClass().getName());
- Assert.assertEquals("getGlobalRoute: routeId cannot be null!", e.getMessage());
- }
- }
-
- @Test
- public void getGlobalRouteTest() throws URISyntaxException {
- QName type = new QName(new URI("actor1"), "actor1");
- RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, type, null);
- String route = "actor1";
-
- routingTable.addGlobalRoute(routeId, route);
-
- String returnedRoute = routingTable.getGlobalRoute(routeId);
-
- Assert.assertEquals(route, returnedRoute);
-
- }
-
- @Test
- public void removeGlobalRouteTest() throws URISyntaxException {
- QName type = new QName(new URI("actorRemove"), "actorRemove");
- RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, type, null);
- String route = "actorRemove";
-
- routingTable.addGlobalRoute(routeId, route);
-
- String returnedRoute = routingTable.getGlobalRoute(routeId);
-
- Assert.assertEquals(route, returnedRoute);
-
- routingTable.removeGlobalRoute(routeId);
-
- String deletedRoute = routingTable.getGlobalRoute(routeId);
-
- Assert.assertNull(deletedRoute);
- }
-
- @Test
- public void addRoutedRpcNullRouteIdTest() {
- try {
- routingTable.addRoutedRpc(null, null);
-
- Assert.fail("Null pointer exception was not thrown.");
- } catch (Exception e) {
- Assert.assertEquals(NullPointerException.class.getName(), e.getClass().getName());
- Assert.assertEquals("addRoute: routeId cannot be null", e.getMessage());
- }
- }
-
- @Test
- public void addRoutedRpcNullRouteTest() {
- try {
- QName type = new QName(new URI("actor1"), "actor1");
- RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, type, null);
-
- routingTable.addRoutedRpc(routeId, null);
-
- Assert.fail("Null pointer exception was not thrown.");
- } catch (Exception e) {
- Assert.assertEquals(NullPointerException.class.getName(), e.getClass().getName());
- Assert.assertEquals("addRoute: route cannot be null", e.getMessage());
- }
- }
-
- @Test
- public void getRoutedRpcNullTest() {
- try {
- routingTable.getRoutedRpc(null);
-
- Assert.fail("Null pointer exception was not thrown.");
- } catch (Exception e) {
- Assert.assertEquals(NullPointerException.class.getName(), e.getClass().getName());
- Assert.assertEquals("getRoutes: routeId cannot be null!", e.getMessage());
- }
- }
-
- @Test
- public void getRoutedRpcTest() throws URISyntaxException {
- QName type = new QName(new URI("actor1"), "actor1");
- RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, type, null);
- String route = "actor1";
-
- routingTable.addRoutedRpc(routeId, route);
-
- Set<String> routes = routingTable.getRoutedRpc(routeId);
-
- Assert.assertEquals(1, routes.size());
- Assert.assertTrue(routes.contains(route));
-
- }
-
- @Test
- public void getLastRoutedRpcTest() throws URISyntaxException {
- QName type = new QName(new URI("first1"), "first1");
- RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, type, null);
- String route = "first1";
-
- routingTable.addRoutedRpc(routeId, route);
-
- String route2 = "second1";
- routingTable.addRoutedRpc(routeId, route2);
-
- String latest = routingTable.getLastAddedRoutedRpc(routeId);
- Assert.assertEquals(route2, latest);
-
- }
-
- @Test
- public void removeRoutedRpcTest() throws URISyntaxException {
- QName type = new QName(new URI("remove"), "remove");
- RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, type, null);
- String route = "remove";
- routingTable.addRoutedRpc(routeId, route);
-
- String latest = routingTable.getLastAddedRoutedRpc(routeId);
- Assert.assertEquals(route, latest);
-
- routingTable.removeRoute(routeId, route);
- String removed = routingTable.getLastAddedRoutedRpc(routeId);
- Assert.assertNull(removed);
- }
-
- @Test
- public void removeRoutedRpcsTest() throws URISyntaxException {
- QName type = new QName(new URI("remove1"), "remove1");
- RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, type, null);
-
- QName type2 = new QName(new URI("remove2"), "remove2");
- RouteIdentifierImpl routeId2 = new RouteIdentifierImpl(null, type2, null);
-
- Set<RpcRouter.RouteIdentifier<?, ?, ?>> routeIds = new HashSet<>();
- routeIds.add(routeId);
- routeIds.add(routeId2);
- String route = "remove1";
-
- routingTable.addRoutedRpcs(routeIds, route);
- String latest1 = routingTable.getLastAddedRoutedRpc(routeId);
- Assert.assertEquals(route, latest1);
-
- String latest2 = routingTable.getLastAddedRoutedRpc(routeId2);
- Assert.assertEquals(route, latest2);
-
- routingTable.removeRoutes(routeIds, route);
- String removed1 = routingTable.getLastAddedRoutedRpc(routeId);
- Assert.assertNull(removed1);
-
- String removed2 = routingTable.getLastAddedRoutedRpc(routeId2);
- Assert.assertNull(removed2);
- }
-
-}
+++ /dev/null
-/*
- * Copyright (c) 2014 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.remote.rpc.registry;
-
-import akka.actor.ActorRef;
-import akka.actor.ActorSystem;
-import akka.testkit.JavaTestKit;
-import junit.framework.Assert;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.mockito.Mockito;
-import org.opendaylight.controller.remote.rpc.RouteIdentifierImpl;
-import org.opendaylight.controller.remote.rpc.messages.AddRoutedRpc;
-import org.opendaylight.controller.remote.rpc.messages.AddRpc;
-import org.opendaylight.controller.remote.rpc.messages.GetRoutedRpc;
-import org.opendaylight.controller.remote.rpc.messages.GetRoutedRpcReply;
-import org.opendaylight.controller.remote.rpc.messages.GetRpc;
-import org.opendaylight.controller.remote.rpc.messages.GetRpcReply;
-import org.opendaylight.controller.remote.rpc.messages.RemoveRoutedRpc;
-import org.opendaylight.controller.remote.rpc.messages.RemoveRpc;
-import org.opendaylight.controller.sal.connector.api.RpcRouter;
-import org.opendaylight.yangtools.yang.common.QName;
-
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.HashSet;
-import java.util.Set;
-
-public class RpcRegistryOldTest {
-
- static ActorSystem system;
-
-
- @BeforeClass
- public static void setup() {
- system = ActorSystem.create();
- }
-
- @AfterClass
- public static void teardown() {
- JavaTestKit.shutdownActorSystem(system);
- system = null;
- }
-
- /**
- This test add, read and remove an entry in global rpc
- */
- @Test
- public void testGlobalRpc() throws URISyntaxException {
- new JavaTestKit(system) {{
- ActorRef rpcRegistry = system.actorOf(RpcRegistryOld.props(Mockito.mock(ClusterWrapper.class)));
- QName type = new QName(new URI("actor1"), "actor1");
- RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, type, null);
- final String route = "actor1";
-
- AddRpc rpcMsg = new AddRpc(routeId, route);
- rpcRegistry.tell(rpcMsg, getRef());
- expectMsgEquals(duration("2 second"), "Success");
-
- GetRpc getRpc = new GetRpc(routeId);
- rpcRegistry.tell(getRpc, getRef());
-
- Boolean getMsg = new ExpectMsg<Boolean>("GetRpcReply") {
- protected Boolean match(Object in) {
- if (in instanceof GetRpcReply) {
- GetRpcReply reply = (GetRpcReply)in;
- return route.equals(reply.getRoutePath());
- } else {
- throw noMatch();
- }
- }
- }.get(); // this extracts the received message
-
- Assert.assertTrue(getMsg);
-
- RemoveRpc removeMsg = new RemoveRpc(routeId);
- rpcRegistry.tell(removeMsg, getRef());
- expectMsgEquals(duration("2 second"), "Success");
-
- rpcRegistry.tell(getRpc, getRef());
-
- Boolean getNullMsg = new ExpectMsg<Boolean>("GetRpcReply") {
- protected Boolean match(Object in) {
- if (in instanceof GetRpcReply) {
- GetRpcReply reply = (GetRpcReply)in;
- return reply.getRoutePath() == null;
- } else {
- throw noMatch();
- }
- }
- }.get();
- Assert.assertTrue(getNullMsg);
- }};
-
- }
-
- /**
- This test add, read and remove an entry in routed rpc
- */
- @Test
- public void testRoutedRpc() throws URISyntaxException {
- new JavaTestKit(system) {{
- ActorRef rpcRegistry = system.actorOf(RpcRegistryOld.props(Mockito.mock(ClusterWrapper.class)));
- QName type = new QName(new URI("actor1"), "actor1");
- RouteIdentifierImpl routeId = new RouteIdentifierImpl(null, type, null);
- final String route = "actor1";
-
- Set<RpcRouter.RouteIdentifier<?, ?, ?>> routeIds = new HashSet<>();
- routeIds.add(routeId);
-
- AddRoutedRpc rpcMsg = new AddRoutedRpc(routeIds, route);
- rpcRegistry.tell(rpcMsg, getRef());
- expectMsgEquals(duration("2 second"), "Success");
-
- GetRoutedRpc getRpc = new GetRoutedRpc(routeId);
- rpcRegistry.tell(getRpc, getRef());
-
- Boolean getMsg = new ExpectMsg<Boolean>("GetRoutedRpcReply") {
- protected Boolean match(Object in) {
- if (in instanceof GetRoutedRpcReply) {
- GetRoutedRpcReply reply = (GetRoutedRpcReply)in;
- return route.equals(reply.getRoutePath());
- } else {
- throw noMatch();
- }
- }
- }.get(); // this extracts the received message
-
- Assert.assertTrue(getMsg);
-
- RemoveRoutedRpc removeMsg = new RemoveRoutedRpc(routeIds, route);
- rpcRegistry.tell(removeMsg, getRef());
- expectMsgEquals(duration("2 second"), "Success");
-
- rpcRegistry.tell(getRpc, getRef());
-
- Boolean getNullMsg = new ExpectMsg<Boolean>("GetRoutedRpcReply") {
- protected Boolean match(Object in) {
- if (in instanceof GetRoutedRpcReply) {
- GetRoutedRpcReply reply = (GetRoutedRpcReply)in;
- return reply.getRoutePath() == null;
- } else {
- throw noMatch();
- }
- }
- }.get();
- Assert.assertTrue(getNullMsg);
- }};
-
- }
-
-}
@BeforeClass
public static void setup() {
- system = ActorSystem.create("opendaylight-rpc", ConfigFactory.load().getConfig("unit-test"));
+ system = ActorSystem.create("opendaylight-rpc", ConfigFactory.load().getConfig("odl-cluster"));
system.actorOf(Props.create(TerminationMonitor.class), "termination-monitor");
store = createStore();
@BeforeClass
public static void setup() throws InterruptedException {
- Thread.sleep(1000);//give some time for previous test to stop the system. Netty port conflict arises otherwise.
- system = ActorSystem.create("opendaylight-rpc", ConfigFactory.load().getConfig("unit-test"));
+ system = ActorSystem.create("opendaylight-rpc", ConfigFactory.load().getConfig("odl-cluster"));
system.actorOf(Props.create(TerminationMonitor.class), "termination-monitor");
gossiper = createGossiper();
--- /dev/null
+/*
+ * Copyright (c) 2014 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.remote.rpc.utils;
+
+
+import akka.actor.ActorRef;
+import akka.actor.ActorSystem;
+import akka.japi.Pair;
+import akka.testkit.JavaTestKit;
+import akka.testkit.TestProbe;
+import com.typesafe.config.ConfigFactory;
+import junit.framework.Assert;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class LatestEntryRoutingLogicTest {
+
+ static ActorSystem system;
+
+ @BeforeClass
+ public static void setup() throws InterruptedException {
+ system = ActorSystem.create("opendaylight-rpc", ConfigFactory.load().getConfig("odl-cluster"));
+ }
+
+ @AfterClass
+ public static void teardown() {
+ JavaTestKit.shutdownActorSystem(system);
+ system = null;
+ }
+
+ @Test
+ public void testRoutingLogic() {
+ List<Pair<ActorRef, Long>> pairList = new ArrayList<>();
+ TestProbe probe1 = new TestProbe(system);
+ TestProbe probe2 = new TestProbe(system);
+ TestProbe probe3 = new TestProbe(system);
+ ActorRef actor1 = probe1.ref();
+ ActorRef actor2 = probe2.ref();
+ ActorRef actor3 = probe3.ref();
+ pairList.add(new Pair<ActorRef, Long>(actor1, 1000L));
+ pairList.add(new Pair<ActorRef, Long>(actor2, 3000L));
+ pairList.add(new Pair<ActorRef, Long>(actor3, 2000L));
+ RoutingLogic logic = new LatestEntryRoutingLogic(pairList);
+ Assert.assertTrue(logic.select().equals(actor2));
+ }
+}
log-remote-lifecycle-events = off
netty.tcp {
- hostname = "localhost"
- port = 2551
+ hostname = "127.0.0.1"
+ port = 2550
}
}
cluster {
- seed-nodes = ["akka.tcp://opendaylight-rpc@localhost:2551"]
+ seed-nodes = ["akka.tcp://opendaylight-rpc@127.0.0.1:2550"]
auto-down-unreachable-after = 10s
}
log-remote-lifecycle-events = off
netty.tcp {
- hostname = "localhost"
+ hostname = "127.0.0.1"
port = 2551
}
}
cluster {
- seed-nodes = ["akka.tcp://opendaylight-rpc@localhost:2551"]
+ seed-nodes = ["akka.tcp://opendaylight-rpc@127.0.0.1:2551"]
auto-down-unreachable-after = 10s
}
log-remote-lifecycle-events = off
netty.tcp {
- hostname = "localhost"
+ hostname = "127.0.0.1"
port = 2552
}
}
cluster {
- seed-nodes = ["akka.tcp://opendaylight-rpc@localhost:2551"]
+ seed-nodes = ["akka.tcp://opendaylight-rpc@127.0.0.1:2551"]
auto-down-unreachable-after = 10s
}
log-remote-lifecycle-events = off
netty.tcp {
- hostname = "localhost"
+ hostname = "127.0.0.1"
port = 2553
}
}
cluster {
- seed-nodes = ["akka.tcp://opendaylight-rpc@localhost:2551"]
+ seed-nodes = ["akka.tcp://opendaylight-rpc@127.0.0.1:2551"]
auto-down-unreachable-after = 10s
}
import com.google.common.base.Optional;
import com.google.common.util.concurrent.CheckedFuture;
-import com.google.common.util.concurrent.ListenableFuture;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
-import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
private NormalizedNode<?, ?> readDataViaTransaction(final DOMDataReadTransaction transaction,
LogicalDatastoreType datastore, YangInstanceIdentifier path) {
LOG.trace("Read " + datastore.name() + " via Restconf: {}", path);
- final ListenableFuture<Optional<NormalizedNode<?, ?>>> listenableFuture = transaction.read(datastore, path);
- if (listenableFuture != null) {
- Optional<NormalizedNode<?, ?>> optional;
- try {
- LOG.debug("Reading result data from transaction.");
- optional = listenableFuture.get();
- } catch (InterruptedException | ExecutionException e) {
- throw new RestconfDocumentedException("Problem to get data from transaction.", e.getCause());
+ final CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> listenableFuture =
+ transaction.read(datastore, path);
- }
- if (optional != null) {
- if (optional.isPresent()) {
- return optional.get();
- }
- }
+ try {
+ Optional<NormalizedNode<?, ?>> optional = listenableFuture.checkedGet();
+ return optional.isPresent() ? optional.get() : null;
+ } catch(ReadFailedException e) {
+ throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList());
}
- return null;
}
private CheckedFuture<Void, TransactionCommitFailedException> postDataViaTransaction(
final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, DataNormalizationOperation<?> root) {
- ListenableFuture<Optional<NormalizedNode<?, ?>>> futureDatastoreData = rWTransaction.read(datastore, path);
+ CheckedFuture<Optional<NormalizedNode<?, ?>>, ReadFailedException> futureDatastoreData =
+ rWTransaction.read(datastore, path);
try {
- final Optional<NormalizedNode<?, ?>> optionalDatastoreData = futureDatastoreData.get();
+ final Optional<NormalizedNode<?, ?>> optionalDatastoreData = futureDatastoreData.checkedGet();
if (optionalDatastoreData.isPresent() && payload.equals(optionalDatastoreData.get())) {
- String errMsg = "Post Configuration via Restconf was not executed because data already exists";
- LOG.trace(errMsg + ":{}", path);
+ LOG.trace("Post Configuration via Restconf was not executed because data already exists :{}", path);
throw new RestconfDocumentedException("Data already exists for path: " + path, ErrorType.PROTOCOL,
ErrorTag.DATA_EXISTS);
}
- } catch (InterruptedException | ExecutionException e) {
- LOG.trace("It wasn't possible to get data loaded from datastore at path " + path);
+ } catch(ReadFailedException e) {
+ LOG.warn("Error reading from datastore with path: " + path, e);
}
ensureParentsByMerge(datastore, path, rWTransaction, root);
try {
currentOp = currentOp.getChild(currentArg);
} catch (DataNormalizationException e) {
- throw new IllegalArgumentException(
- String.format("Invalid child encountered in path %s", normalizedPath), e);
+ throw new RestconfDocumentedException(
+ String.format("Error normalizing data for path %s", normalizedPath), e);
}
currentArguments.add(currentArg);
YangInstanceIdentifier currentPath = YangInstanceIdentifier.create(currentArguments);
- final Boolean exists;
-
try {
- CheckedFuture<Boolean, ReadFailedException> future =
- rwTx.exists(store, currentPath);
- exists = future.checkedGet();
+ boolean exists = rwTx.exists(store, currentPath).checkedGet();
+ if (!exists && iterator.hasNext()) {
+ rwTx.merge(store, currentPath, currentOp.createDefault(currentArg));
+ }
} catch (ReadFailedException e) {
LOG.error("Failed to read pre-existing data from store {} path {}", store, currentPath, e);
- throw new IllegalStateException("Failed to read pre-existing data", e);
- }
-
-
- if (!exists && iterator.hasNext()) {
- rwTx.merge(store, currentPath, currentOp.createDefault(currentArg));
+ throw new RestconfDocumentedException("Failed to read pre-existing data", e);
}
}
}
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+import java.util.Collection;
import java.util.List;
+
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response.Status;
+
import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
+import org.opendaylight.yangtools.yang.common.RpcError;
/**
* Unchecked exception to communicate error information, as defined in the ietf restcong draft, to be sent to the
}
/**
- * Constructs an instance with an error message and exception cause. The stack trace of the exception is included in
- * the error info.
+ * Constructs an instance with an error message and exception cause.
+ * The stack trace of the exception is included in the error info.
*
* @param message
* A string which provides a plain text string describing the error.
/**
* Constructs an instance with the given errors.
*/
- public RestconfDocumentedException(List<RestconfError> errors) {
- this.errors = ImmutableList.copyOf(errors);
- Preconditions.checkArgument(!this.errors.isEmpty(), "RestconfError list can't be empty");
+ public RestconfDocumentedException(String message, Throwable cause, List<RestconfError> errors) {
+ super(message, cause);
+ if(!errors.isEmpty()) {
+ this.errors = ImmutableList.copyOf(errors);
+ } else {
+ this.errors = ImmutableList.of(new RestconfError(RestconfError.ErrorType.APPLICATION,
+ RestconfError.ErrorTag.OPERATION_FAILED, message));
+ }
+
status = null;
}
+ /**
+ * Constructs an instance with the given RpcErrors.
+ */
+ public RestconfDocumentedException(String message, Throwable cause, Collection<RpcError> rpcErrors) {
+ this(message, cause, convertToRestconfErrors(rpcErrors));
+ }
+
/**
* Constructs an instance with an HTTP status and no error information.
*
status = null;
}
+ private static List<RestconfError> convertToRestconfErrors(Collection<RpcError> rpcErrors) {
+ List<RestconfError> errorList = Lists.newArrayList();
+ if(rpcErrors != null) {
+ for (RpcError rpcError : rpcErrors) {
+ errorList.add(new RestconfError(rpcError));
+ }
+ }
+
+ return errorList;
+ }
+
+
public List<RestconfError> getErrors() {
return errors;
}
RESOURCE_DENIED("resource-denied", 409 /* Conflict */),
ROLLBACK_FAILED("rollback-failed", 500 /* INTERNAL_SERVER_ERROR */),
DATA_EXISTS("data-exists", 409 /* Conflict */),
- DATA_MISSING("data-missing", 409 /* Conflict */),
+ DATA_MISSING("data-missing", 404 /* Resource Not Found */),
OPERATION_NOT_SUPPORTED("operation-not-supported", 501 /* Not Implemented */),
OPERATION_FAILED("operation-failed", 500 /* INTERNAL_SERVER_ERROR */),
PARTIAL_OPERATION("partial-operation", 500 /* INTERNAL_SERVER_ERROR */),
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import org.apache.commons.lang3.StringUtils;
import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
import org.opendaylight.controller.sal.rest.api.Draft02;
import org.opendaylight.yangtools.concepts.Codec;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.QNameModule;
-import org.opendaylight.yangtools.yang.common.RpcError;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.data.api.CompositeNode;
import org.opendaylight.yangtools.yang.data.api.MutableCompositeNode;
private void checkRpcSuccessAndThrowException(final RpcResult<CompositeNode> rpcResult) {
if (rpcResult.isSuccessful() == false) {
- Collection<RpcError> rpcErrors = rpcResult.getErrors();
- if (rpcErrors == null || rpcErrors.isEmpty()) {
- throw new RestconfDocumentedException(
- "The operation was not successful and there were no RPC errors returned", ErrorType.RPC,
- ErrorTag.OPERATION_FAILED);
- }
-
- List<RestconfError> errorList = Lists.newArrayList();
- for (RpcError rpcError : rpcErrors) {
- errorList.add(new RestconfError(rpcError));
- }
-
- throw new RestconfDocumentedException(errorList);
+ throw new RestconfDocumentedException("The operation was not successful", null,
+ rpcResult.getErrors());
}
}
iiWithData.getSchemaNode());
YangInstanceIdentifier normalizedII;
+ if (mountPoint != null) {
+ normalizedII = new DataNormalizer(mountPoint.getSchemaContext()).toNormalized(
+ iiWithData.getInstanceIdentifier());
+ } else {
+ normalizedII = controllerContext.toNormalized(iiWithData.getInstanceIdentifier());
+ }
- try {
- if (mountPoint != null) {
- normalizedII = new DataNormalizer(mountPoint.getSchemaContext()).toNormalized(iiWithData
- .getInstanceIdentifier());
- broker.commitConfigurationDataPut(mountPoint, normalizedII, datastoreNormalizedNode).get();
- } else {
- normalizedII = controllerContext.toNormalized(iiWithData.getInstanceIdentifier());
- broker.commitConfigurationDataPut(normalizedII, datastoreNormalizedNode).get();
+ /*
+ * There is a small window where another write transaction could be updating the same data
+ * simultaneously and we get an OptimisticLockFailedException. This error is likely
+ * transient and The WriteTransaction#submit API docs state that a retry will likely
+ * succeed. So we'll try again if that scenario occurs. If it fails a third time then it
+ * probably will never succeed so we'll fail in that case.
+ *
+ * By retrying we're attempting to hide the internal implementation of the data store and
+ * how it handles concurrent updates from the restconf client. The client has instructed us
+ * to put the data and we should make every effort to do so without pushing optimistic lock
+ * failures back to the client and forcing them to handle it via retry (and having to
+ * document the behavior).
+ */
+ int tries = 2;
+ while(true) {
+ try {
+ if (mountPoint != null) {
+ broker.commitConfigurationDataPut(mountPoint, normalizedII,
+ datastoreNormalizedNode).checkedGet();
+ } else {
+ broker.commitConfigurationDataPut(normalizedII,
+ datastoreNormalizedNode).checkedGet();
+ }
+
+ break;
+ } catch (TransactionCommitFailedException e) {
+ if(e instanceof OptimisticLockFailedException) {
+ if(--tries <= 0) {
+ LOG.debug("Got OptimisticLockFailedException on last try - failing");
+ throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList());
+ }
+
+ LOG.debug("Got OptimisticLockFailedException - trying again");
+ } else {
+ throw new RestconfDocumentedException(e.getMessage(), e, e.getErrorList());
+ }
}
- } catch (Exception e) {
- throw new RestconfDocumentedException("Error updating data", e);
}
return Response.status(Status.OK).build();
normalizedII = controllerContext.toNormalized(iiWithData.getInstanceIdentifier());
broker.commitConfigurationDataPost(normalizedII, datastoreNormalizedData);
}
+ } catch(RestconfDocumentedException e) {
+ throw e;
} catch (Exception e) {
throw new RestconfDocumentedException("Error creating data", e);
}
normalizedII = controllerContext.toNormalized(iiWithData.getInstanceIdentifier());
broker.commitConfigurationDataPost(normalizedII, datastoreNormalizedData);
}
+ } catch(RestconfDocumentedException e) {
+ throw e;
} catch (Exception e) {
throw new RestconfDocumentedException("Error creating data", e);
}
private CompositeNode normalizeNode(final Node<?> node, final DataSchemaNode schema, final DOMMountPoint mountPoint) {
if (schema == null) {
- QName nodeType = node == null ? null : node.getNodeType();
- String localName = nodeType == null ? null : nodeType.getLocalName();
+ String localName = node == null ? null :
+ node instanceof NodeWrapper ? ((NodeWrapper<?>)node).getLocalName() :
+ node.getNodeType().getLocalName();
throw new RestconfDocumentedException("Data schema node was not found for " + localName,
ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE);
restconfImpl.invokeRpc("toaster:cancel-toast", "", uriInfo);
fail("Expected an exception to be thrown.");
} catch (RestconfDocumentedException e) {
- verifyRestconfDocumentedException(e, 0, ErrorType.RPC, ErrorTag.OPERATION_FAILED,
+ verifyRestconfDocumentedException(e, 0, ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED,
Optional.<String> absent(), Optional.<String> absent());
}
}
import com.google.common.base.Optional;
import com.google.common.util.concurrent.CheckedFuture;
+
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URISyntaxException;
+
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
+
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.BeforeClass;
import org.junit.Test;
+import org.opendaylight.controller.md.sal.common.api.data.OptimisticLockFailedException;
import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
assertEquals(200, put(uri, MediaType.APPLICATION_XML, xmlData3));
}
+ @Test
+ public void putWithOptimisticLockFailedException() throws UnsupportedEncodingException {
+
+ String uri = "/config/ietf-interfaces:interfaces/interface/eth0";
+
+ doThrow(OptimisticLockFailedException.class).
+ when(brokerFacade).commitConfigurationDataPut(
+ any(YangInstanceIdentifier.class), any(NormalizedNode.class));
+
+ assertEquals(500, put(uri, MediaType.APPLICATION_XML, xmlData));
+
+ doThrow(OptimisticLockFailedException.class).doReturn(mock(CheckedFuture.class)).
+ when(brokerFacade).commitConfigurationDataPut(
+ any(YangInstanceIdentifier.class), any(NormalizedNode.class));
+
+ assertEquals(200, put(uri, MediaType.APPLICATION_XML, xmlData));
+ }
+
+ @Test
+ public void putWithTransactionCommitFailedException() throws UnsupportedEncodingException {
+
+ String uri = "/config/ietf-interfaces:interfaces/interface/eth0";
+
+ doThrow(TransactionCommitFailedException.class).
+ when(brokerFacade).commitConfigurationDataPut(
+ any(YangInstanceIdentifier.class), any(NormalizedNode.class));
+
+ assertEquals(500, put(uri, MediaType.APPLICATION_XML, xmlData));
+ }
+
private int put(String uri, String mediaType, String data) throws UnsupportedEncodingException {
return target(uri).request(mediaType).put(Entity.entity(data, mediaType)).getStatus();
}
public void testToJsonResponseWithDataMissingErrorTag() throws Exception {
testJsonResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.DATA_MISSING),
- Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING, "mock error", null, null);
+ Status.NOT_FOUND, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING, "mock error", null, null);
}
@Test
List<RestconfError> errorList = Arrays.asList(new RestconfError(ErrorType.APPLICATION, ErrorTag.LOCK_DENIED,
"mock error1"), new RestconfError(ErrorType.RPC, ErrorTag.ROLLBACK_FAILED, "mock error2"));
- stageMockEx(new RestconfDocumentedException(errorList));
+ stageMockEx(new RestconfDocumentedException("mock", null, errorList));
Response resp = target("/operational/foo").request(MediaType.APPLICATION_JSON).get();
public void testToXMLResponseWithDataMissingErrorTag() throws Exception {
testXMLResponse(new RestconfDocumentedException("mock error", ErrorType.PROTOCOL, ErrorTag.DATA_MISSING),
- Status.CONFLICT, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING, "mock error", null, null);
+ Status.NOT_FOUND, ErrorType.PROTOCOL, ErrorTag.DATA_MISSING, "mock error", null, null);
}
@Test
List<RestconfError> errorList = Arrays.asList(new RestconfError(ErrorType.APPLICATION, ErrorTag.LOCK_DENIED,
"mock error1"), new RestconfError(ErrorType.RPC, ErrorTag.ROLLBACK_FAILED, "mock error2"));
- stageMockEx(new RestconfDocumentedException(errorList));
+ stageMockEx(new RestconfDocumentedException("mock", null, errorList));
Response resp = target("/operational/foo").request(MediaType.APPLICATION_XML).get();
lookUpMap.put("resource-denied", 409);
lookUpMap.put("rollback-failed", 500);
lookUpMap.put("data-exists", 409);
- lookUpMap.put("data-missing", 409);
+ lookUpMap.put("data-missing", 404);
lookUpMap.put("operation-not-supported", 501);
lookUpMap.put("operation-failed", 500);
lookUpMap.put("partial-operation", 500);
dataChangeListenerRegistration.close();
rpcRegistration.close();
runtimeReg.close();
- opendaylightToaster.close();
+ closeQuietly(opendaylightToaster);
log.info("Toaster provider (instance {}) torn down.", this);
}
+
+ private void closeQuietly(final AutoCloseable resource) {
+ try {
+ resource.close();
+ } catch (final Exception e) {
+ log.debug("Ignoring exception while closing {}", resource, e);
+ }
+ }
}
AutoCloseable ret = new AutoCloseableToaster();
}
public static boolean flowEquals(Flow statsFlow, Flow storedFlow) {
+ if (statsFlow == null || storedFlow == null) {
+ return false;
+ }
if (statsFlow.getClass() != storedFlow.getClass()) {
return false;
}
} else if(!statsFlow.getContainerName().equals(storedFlow.getContainerName())) {
return false;
}
- if (statsFlow.getMatch()== null) {
- if (storedFlow.getMatch() != null) {
+ if (storedFlow.getPriority() == null) {
+ if (statsFlow.getPriority() != null && statsFlow.getPriority()!= 0x8000) {
return false;
}
- } //else if(!statsFlow.getMatch().equals(storedFlow.getMatch())) {
- else if(!matchEquals(statsFlow.getMatch(), storedFlow.getMatch())) {
+ } else if(!statsFlow.getPriority().equals(storedFlow.getPriority())) {
return false;
}
- if (storedFlow.getPriority() == null) {
- if (statsFlow.getPriority() != null && statsFlow.getPriority()!= 0x8000) {
+ if (statsFlow.getMatch()== null) {
+ if (storedFlow.getMatch() != null) {
return false;
}
- } else if(!statsFlow.getPriority().equals(storedFlow.getPriority())) {
+ } else if(!matchEquals(statsFlow.getMatch(), storedFlow.getMatch())) {
return false;
}
if (statsFlow.getTableId() == null) {
*/
package org.opendaylight.controller.md.statistics.manager;
+import java.math.BigInteger;
import java.util.Collection;
+import java.util.Collections;
import java.util.Map.Entry;
import org.opendaylight.controller.md.sal.common.api.data.DataChangeEvent;
import org.opendaylight.controller.sal.binding.api.data.DataBrokerService;
import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCookieMapping;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.nodes.node.table.FlowCookieMap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.nodes.node.table.FlowCookieMapBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.nodes.node.table.FlowCookieMapKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.flow.and.statistics.map.list.FlowAndStatisticsMapList;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.flow.and.statistics.map.list.FlowAndStatisticsMapListBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.statistics.rev130819.flow.statistics.FlowStatisticsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowCookie;
import org.opendaylight.yang.gen.v1.urn.opendaylight.model.statistics.types.rev130925.GenericStatistics;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.base.Optional;
+
final class FlowStatsTracker extends AbstractListeningStatsTracker<FlowAndStatisticsMapList, FlowStatsEntry> {
- private static final Logger logger = LoggerFactory.getLogger(FlowStatsTracker.class);
+ private static final Logger LOG = LoggerFactory.getLogger(FlowStatsTracker.class);
+ private static final String ALIEN_SYSTEM_FLOW_ID = "#UF$TABLE*";
private final OpendaylightFlowStatisticsService flowStatsService;
private FlowTableStatsTracker flowTableStats;
private int unaccountedFlowsCounter = 1;
+
FlowStatsTracker(final OpendaylightFlowStatisticsService flowStatsService, final FlowCapableContext context) {
super(context);
this.flowStatsService = flowStatsService;
@Override
protected void cleanupSingleStat(final DataModificationTransaction trans, final FlowStatsEntry item) {
- InstanceIdentifier<?> flowRef = getNodeIdentifierBuilder()
- .augmentation(FlowCapableNode.class)
- .child(Table.class, new TableKey(item.getTableId()))
- .child(Flow.class,item.getFlow().getKey())
- .augmentation(FlowStatisticsData.class).toInstance();
+ KeyedInstanceIdentifier<Flow, FlowKey> flowRef = getNodeIdentifier()
+ .augmentation(FlowCapableNode.class)
+ .child(Table.class, new TableKey(item.getTableId()))
+ .child(Flow.class, item.getFlow().getKey());
trans.removeOperationalData(flowRef);
}
FlowStatisticsDataBuilder flowStatisticsData = new FlowStatisticsDataBuilder();
- FlowBuilder flow = new FlowBuilder(map);
- if(map.getFlowId() != null) {
- flow.setId(new FlowId(map.getFlowId().getValue()));
+ FlowBuilder flowBuilder = new FlowBuilder(map);
+ if (map.getFlowId() != null) {
+ flowBuilder.setId(new FlowId(map.getFlowId().getValue()));
}
- if(map.getFlowId()!= null) {
- flow.setKey(new FlowKey(new FlowId(map.getKey().getFlowId().getValue())));
+ if (map.getFlowId() != null) {
+ flowBuilder.setKey(new FlowKey(new FlowId(map.getKey().getFlowId().getValue())));
}
- Flow flowRule = flow.build();
+ Flow flowRule = flowBuilder.build();
FlowAndStatisticsMapListBuilder stats = new FlowAndStatisticsMapListBuilder();
stats.setByteCount(map.getByteCount());
flowStatisticsData.setFlowStatistics(flowStatistics.build());
- logger.debug("Flow : {}",flowRule.toString());
- logger.debug("Statistics to augment : {}",flowStatistics.build().toString());
+ LOG.debug("Flow : {}",flowRule.toString());
+ LOG.debug("Statistics to augment : {}",flowStatistics.build().toString());
InstanceIdentifier<Table> tableRef = getNodeIdentifierBuilder()
- .augmentation(FlowCapableNode.class).child(Table.class, new TableKey(tableId)).toInstance();
-
- //TODO: Not a good way to do it, need to figure out better way.
- //TODO: major issue in any alternate approach is that flow key is incrementally assigned
- //to the flows stored in data store.
- // Augment same statistics to all the matching masked flow
- Table table= (Table)trans.readConfigurationData(tableRef);
- if(table != null){
- for(Flow existingFlow : table.getFlow()){
- logger.debug("Existing flow in data store : {}",existingFlow.toString());
- if(FlowComparator.flowEquals(flowRule,existingFlow)){
- InstanceIdentifier<Flow> flowRef = getNodeIdentifierBuilder()
- .augmentation(FlowCapableNode.class)
- .child(Table.class, new TableKey(tableId))
- .child(Flow.class,existingFlow.getKey()).toInstance();
- flow.setKey(existingFlow.getKey());
- flow.addAugmentation(FlowStatisticsData.class, flowStatisticsData.build());
- logger.debug("Found matching flow in the datastore, augmenting statistics");
- // Update entry with timestamp of latest response
- FlowStatsEntry flowStatsEntry = new FlowStatsEntry(tableId,flow.build());
- trans.putOperationalData(flowRef, flow.build());
- return flowStatsEntry;
- }
+ .augmentation(FlowCapableNode.class)
+ .child(Table.class, new TableKey(tableId)).toInstance();
+
+ final FlowCookie flowCookie = flowRule.getCookie() != null
+ ? flowRule.getCookie() : new FlowCookie(BigInteger.ZERO);
+ final InstanceIdentifier<FlowCookieMap> flowCookieRef = tableRef
+ .augmentation(FlowCookieMapping.class)
+ .child(FlowCookieMap.class, new FlowCookieMapKey(flowCookie));
+
+ FlowCookieMap cookieMap = (FlowCookieMap) trans.readOperationalData(flowCookieRef);
+
+ /* find flowKey in FlowCookieMap from DataStore/OPERATIONAL */
+ Optional<FlowKey> flowKey = this.getExistFlowKey(flowRule, tableRef, trans, cookieMap);
+ if ( ! flowKey.isPresent()) {
+ /* DataStore/CONFIG For every first statistic needs to be created */
+ flowKey = this.getFlowKeyFromExistFlow(flowRule, tableRef, trans);
+ if ( ! flowKey.isPresent()) {
+ /* Alien flow */
+ flowKey = this.makeAlienFlowKey(flowRule);
}
+ cookieMap = applyNewFlowKey(cookieMap, flowKey, flowCookie);
+ trans.putOperationalData(flowCookieRef, cookieMap);
}
- table = (Table)trans.readOperationalData(tableRef);
- if(table != null){
- for(Flow existingFlow : table.getFlow()){
- FlowStatisticsData augmentedflowStatisticsData = existingFlow.getAugmentation(FlowStatisticsData.class);
- if(augmentedflowStatisticsData != null){
- FlowBuilder existingOperationalFlow = new FlowBuilder();
- existingOperationalFlow.fieldsFrom(augmentedflowStatisticsData.getFlowStatistics());
- logger.debug("Existing unaccounted flow in operational data store : {}",existingFlow.toString());
- if(FlowComparator.flowEquals(flowRule,existingOperationalFlow.build())){
- InstanceIdentifier<Flow> flowRef = getNodeIdentifierBuilder()
- .augmentation(FlowCapableNode.class)
- .child(Table.class, new TableKey(tableId))
- .child(Flow.class,existingFlow.getKey()).toInstance();
- flow.setKey(existingFlow.getKey());
- flow.addAugmentation(FlowStatisticsData.class, flowStatisticsData.build());
- logger.debug("Found matching unaccounted flow in the operational datastore, augmenting statistics");
- // Update entry with timestamp of latest response
- FlowStatsEntry flowStatsEntry = new FlowStatsEntry(tableId,flow.build());
- trans.putOperationalData(flowRef, flow.build());
- return flowStatsEntry;
- }
- }
- }
- }
-
- String flowKey = "#UF$TABLE*"+Short.toString(tableId)+"*"+Integer.toString(this.unaccountedFlowsCounter);
- this.unaccountedFlowsCounter++;
- FlowKey newFlowKey = new FlowKey(new FlowId(flowKey));
- InstanceIdentifier<Flow> flowRef = getNodeIdentifierBuilder().augmentation(FlowCapableNode.class)
- .child(Table.class, new TableKey(tableId))
- .child(Flow.class,newFlowKey).toInstance();
- flow.setKey(newFlowKey);
- flow.addAugmentation(FlowStatisticsData.class, flowStatisticsData.build());
- logger.debug("Flow {} is not present in config data store, augmenting statistics as an unaccounted flow",
- flow.build());
+ InstanceIdentifier<Flow> flowRef = getNodeIdentifierBuilder()
+ .augmentation(FlowCapableNode.class)
+ .child(Table.class, new TableKey(tableId))
+ .child(Flow.class, flowKey.get()).toInstance();
+ flowBuilder.setKey(flowKey.get());
+ flowBuilder.addAugmentation(FlowStatisticsData.class, flowStatisticsData.build());
// Update entry with timestamp of latest response
- flow.setKey(newFlowKey);
- FlowStatsEntry flowStatsEntry = new FlowStatsEntry(tableId,flow.build());
- trans.putOperationalData(flowRef, flow.build());
+ flowBuilder.setKey(flowKey.get());
+ FlowStatsEntry flowStatsEntry = new FlowStatsEntry(tableId, flowBuilder.build());
+ trans.putOperationalData(flowRef, flowBuilder.build());
return flowStatsEntry;
}
// FIXME: it does not make sense to trigger this before sendAllFlowTablesStatisticsRequest()
// comes back -- we do not have any tables anyway.
final Collection<TableKey> tables = flowTableStats.getTables();
- logger.debug("Node {} supports {} table(s)", this.getNodeRef(), tables.size());
+ LOG.debug("Node {} supports {} table(s)", this.getNodeRef(), tables.size());
for (final TableKey key : tables) {
- logger.debug("Send aggregate stats request for flow table {} to node {}", key.getId(), this.getNodeRef());
+ LOG.debug("Send aggregate stats request for flow table {} to node {}", key.getId(), this.getNodeRef());
this.requestAggregateFlows(key);
}
for (Entry<InstanceIdentifier<?>, DataObject> e : change.getCreatedConfigurationData().entrySet()) {
if (Flow.class.equals(e.getKey().getTargetType())) {
final Flow flow = (Flow) e.getValue();
- logger.debug("Key {} triggered request for flow {}", e.getKey(), flow);
+ LOG.debug("Key {} triggered request for flow {}", e.getKey(), flow);
requestFlow(flow);
} else {
- logger.debug("Ignoring key {}", e.getKey());
+ LOG.debug("Ignoring key {}", e.getKey());
}
}
if (Flow.class.equals(key.getTargetType())) {
@SuppressWarnings("unchecked")
final InstanceIdentifier<Flow> flow = (InstanceIdentifier<Flow>)key;
- logger.debug("Key {} triggered remove of Flow from operational space.", key);
+ LOG.debug("Key {} triggered remove of Flow from operational space.", key);
trans.removeOperationalData(flow);
}
}
@Override
public void start(final DataBrokerService dbs) {
if (flowStatsService == null) {
- logger.debug("No Flow Statistics service, not subscribing to flows on node {}", getNodeIdentifier());
+ LOG.debug("No Flow Statistics service, not subscribing to flows on node {}", getNodeIdentifier());
return;
}
super.start(dbs);
}
+
+ /* Returns Exist FlowKey from exist FlowCookieMap identified by cookie
+ * and by switch flow identification (priority and match)*/
+ private Optional<FlowKey> getExistFlowKey(final Flow flowRule, final InstanceIdentifier<Table> tableRef,
+ final DataModificationTransaction trans, final FlowCookieMap cookieMap) {
+
+ if (cookieMap != null) {
+ for (FlowId flowId : cookieMap.getFlowIds()) {
+ InstanceIdentifier<Flow> flowIdent = tableRef.child(Flow.class, new FlowKey(flowId));
+ if (flowId.getValue().startsWith(ALIEN_SYSTEM_FLOW_ID)) {
+ LOG.debug("Search for flow in the operational datastore by flowID: {} ", flowIdent);
+ Flow readedFlow = (Flow) trans.readOperationalData(flowIdent);
+ if (FlowComparator.flowEquals(flowRule, readedFlow)) {
+ return Optional.<FlowKey> of(new FlowKey(flowId));
+ }
+ } else {
+ LOG.debug("Search for flow in the configuration datastore by flowID: {} ", flowIdent);
+ Flow readedFlow = (Flow) trans.readConfigurationData(flowIdent);
+ if (FlowComparator.flowEquals(flowRule, readedFlow)) {
+ return Optional.<FlowKey> of(new FlowKey(flowId));
+ }
+ }
+ }
+ LOG.debug("Flow was not found in the datastore. Flow {} ", flowRule);
+ }
+ return Optional.absent();
+ }
+
+ /* Returns FlowKey from existing Flow in DataStore/CONFIGURATIONAL which is identified by cookie
+ * and by switch flow identification (priority and match) */
+ private Optional<FlowKey> getFlowKeyFromExistFlow(final Flow flowRule, final InstanceIdentifier<Table> tableRef,
+ final DataModificationTransaction trans) {
+
+ /* Try to find it in DataSotre/CONFIG */
+ Table table= (Table)trans.readConfigurationData(tableRef);
+ if(table != null) {
+ for(Flow existingFlow : table.getFlow()) {
+ LOG.debug("Existing flow in data store : {}",existingFlow.toString());
+ if(FlowComparator.flowEquals(flowRule,existingFlow)){
+ return Optional.<FlowKey> of(new FlowKey(existingFlow.getId()));
+ }
+ }
+ }
+ return Optional.absent();
+ }
+
+ /* Returns FlowKey which doesn't exist in any DataStore for now */
+ private Optional<FlowKey> makeAlienFlowKey(final Flow flowRule) {
+
+ StringBuilder sBuilder = new StringBuilder(ALIEN_SYSTEM_FLOW_ID)
+ .append(flowRule.getTableId()).append("-").append(this.unaccountedFlowsCounter);
+ this.unaccountedFlowsCounter++;
+ final FlowId flowId = new FlowId(sBuilder.toString());
+ return Optional.<FlowKey> of(new FlowKey(flowId));
+ }
+
+ /* Build new whole FlowCookieMap or add new flowKey */
+ private FlowCookieMap applyNewFlowKey(FlowCookieMap flowCookieMap, final Optional<FlowKey> flowKey,
+ final FlowCookie flowCookie) {
+ if (flowCookieMap != null) {
+ flowCookieMap.getFlowIds().add(flowKey.get().getId());
+ } else {
+ final FlowCookieMapBuilder flowCookieMapBuilder = new FlowCookieMapBuilder();
+ flowCookieMapBuilder.setCookie(flowCookie);
+ flowCookieMapBuilder.setFlowIds(Collections.singletonList(flowKey.get().getId()));
+ flowCookieMap = flowCookieMapBuilder.build();
+ }
+ return flowCookieMap;
+ }
}
}
@Override
- protected void cleanupSingleStat(DataModificationTransaction trans, NodeConnectorStatisticsAndPortNumberMap item) {
+ protected void cleanupSingleStat(final DataModificationTransaction trans, final NodeConnectorStatisticsAndPortNumberMap item) {
// TODO Auto-generated method stub
}
@Override
- protected NodeConnectorStatisticsAndPortNumberMap updateSingleStat(DataModificationTransaction trans, NodeConnectorStatisticsAndPortNumberMap item) {
+ protected NodeConnectorStatisticsAndPortNumberMap updateSingleStat(final DataModificationTransaction trans, final NodeConnectorStatisticsAndPortNumberMap item) {
FlowCapableNodeConnectorStatisticsBuilder statisticsBuilder
= new FlowCapableNodeConnectorStatisticsBuilder();
statisticsBuilder.setBytes(item.getBytes());
statisticsDataBuilder.setFlowCapableNodeConnectorStatistics(statisticsBuilder.build());
- InstanceIdentifier<NodeConnector> nodeConnectorRef = getNodeIdentifierBuilder()
- .child(NodeConnector.class, new NodeConnectorKey(item.getNodeConnectorId())).build();
+ final NodeConnectorKey key = new NodeConnectorKey(item.getNodeConnectorId());
+ final InstanceIdentifier<NodeConnector> nodeConnectorRef = getNodeIdentifier().child(NodeConnector.class, key);
// FIXME: can we bypass this read?
NodeConnector nodeConnector = (NodeConnector)trans.readOperationalData(nodeConnectorRef);
if(nodeConnector != null){
final FlowCapableNodeConnectorStatisticsData stats = statisticsDataBuilder.build();
logger.debug("Augmenting port statistics {} to port {}",stats,nodeConnectorRef.toString());
- NodeConnectorBuilder nodeConnectorBuilder = new NodeConnectorBuilder();
- nodeConnectorBuilder.addAugmentation(FlowCapableNodeConnectorStatisticsData.class, stats);
+ NodeConnectorBuilder nodeConnectorBuilder = new NodeConnectorBuilder()
+ .setKey(key).setId(item.getNodeConnectorId())
+ .addAugmentation(FlowCapableNodeConnectorStatisticsData.class, stats);
trans.putOperationalData(nodeConnectorRef, nodeConnectorBuilder.build());
}
import static org.opendaylight.md.controller.topology.manager.FlowCapableNodeMapping.toTopologyNode;
import static org.opendaylight.md.controller.topology.manager.FlowCapableNodeMapping.toTopologyNodeId;
-import org.opendaylight.controller.md.sal.binding.util.TypeSafeDataReader;
-import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnectorUpdated;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeUpdated;
import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.topology.discovery.rev130819.FlowTopologyDiscoveryListener;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPointKey;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-
-import com.google.common.base.Preconditions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
class FlowCapableTopologyExporter implements FlowTopologyDiscoveryListener, OpendaylightInventoryListener {
+
+ private final Logger LOG = LoggerFactory.getLogger(FlowCapableTopologyExporter.class);
private final InstanceIdentifier<Topology> topology;
private final OperationProcessor processor;
@Override
public void onNodeRemoved(final NodeRemoved notification) {
+
+ final NodeId nodeId = toTopologyNodeId(getNodeKey(notification.getNodeRef()).getId());
+ final InstanceIdentifier<Node> nodeInstance = toNodeIdentifier(notification.getNodeRef());
+
processor.enqueueOperation(new TopologyOperation() {
@Override
- public void applyOperation(final DataModificationTransaction transaction) {
- NodeId nodeId = toTopologyNodeId(getNodeKey(notification.getNodeRef()).getId());
- InstanceIdentifier<Node> nodeInstance = toNodeIdentifier(notification.getNodeRef());
- transaction.removeOperationalData(nodeInstance);
- removeAffectedLinks(transaction, nodeId);
+ public void applyOperation(final ReadWriteTransaction transaction) {
+ removeAffectedLinks(nodeId);
+ }
+ });
+
+ processor.enqueueOperation(new TopologyOperation() {
+ @Override
+ public void applyOperation(ReadWriteTransaction transaction) {
+ transaction.delete(LogicalDatastoreType.OPERATIONAL, nodeInstance);
}
});
}
if (fcnu != null) {
processor.enqueueOperation(new TopologyOperation() {
@Override
- public void applyOperation(final DataModificationTransaction transaction) {
- Node node = toTopologyNode(toTopologyNodeId(notification.getId()), notification.getNodeRef());
- InstanceIdentifier<Node> path = getNodePath(toTopologyNodeId(notification.getId()));
- transaction.putOperationalData(path, node);
+ public void applyOperation(final ReadWriteTransaction transaction) {
+ final Node node = toTopologyNode(toTopologyNodeId(notification.getId()), notification.getNodeRef());
+ final InstanceIdentifier<Node> path = getNodePath(toTopologyNodeId(notification.getId()));
+ transaction.put(LogicalDatastoreType.OPERATIONAL, path, node);
}
});
}
@Override
public void onNodeConnectorRemoved(final NodeConnectorRemoved notification) {
+
+ final InstanceIdentifier<TerminationPoint> tpInstance = toTerminationPointIdentifier(notification
+ .getNodeConnectorRef());
+
processor.enqueueOperation(new TopologyOperation() {
@Override
- public void applyOperation(final DataModificationTransaction transaction) {
- InstanceIdentifier<TerminationPoint> tpInstance = toTerminationPointIdentifier(notification
- .getNodeConnectorRef());
- TpId tpId = toTerminationPointId(getNodeConnectorKey(notification.getNodeConnectorRef()).getId());
+ public void applyOperation(final ReadWriteTransaction transaction) {
+ final TpId tpId = toTerminationPointId(getNodeConnectorKey(notification.getNodeConnectorRef()).getId());
+ removeAffectedLinks(tpId);
+ }
+ });
- transaction.removeOperationalData(tpInstance);
- removeAffectedLinks(transaction, tpId);
+ processor.enqueueOperation(new TopologyOperation() {
+ @Override
+ public void applyOperation(ReadWriteTransaction transaction) {
+ transaction.delete(LogicalDatastoreType.OPERATIONAL, tpInstance);
}
});
}
if (fcncu != null) {
processor.enqueueOperation(new TopologyOperation() {
@Override
- public void applyOperation(final DataModificationTransaction transaction) {
- NodeId nodeId = toTopologyNodeId(getNodeKey(notification.getNodeConnectorRef()).getId());
+ public void applyOperation(final ReadWriteTransaction transaction) {
+ final NodeId nodeId = toTopologyNodeId(getNodeKey(notification.getNodeConnectorRef()).getId());
TerminationPoint point = toTerminationPoint(toTerminationPointId(notification.getId()),
notification.getNodeConnectorRef());
- InstanceIdentifier<TerminationPoint> path = tpPath(nodeId, point.getKey().getTpId());
-
- transaction.putOperationalData(path, point);
+ final InstanceIdentifier<TerminationPoint> path = tpPath(nodeId, point.getKey().getTpId());
+ transaction.put(LogicalDatastoreType.OPERATIONAL, path, point);
if ((fcncu.getState() != null && fcncu.getState().isLinkDown())
|| (fcncu.getConfiguration() != null && fcncu.getConfiguration().isPORTDOWN())) {
- removeAffectedLinks(transaction, point.getTpId());
+ removeAffectedLinks(point.getTpId());
}
}
});
public void onLinkDiscovered(final LinkDiscovered notification) {
processor.enqueueOperation(new TopologyOperation() {
@Override
- public void applyOperation(final DataModificationTransaction transaction) {
- Link link = toTopologyLink(notification);
- InstanceIdentifier<Link> path = linkPath(link);
- transaction.putOperationalData(path, link);
+ public void applyOperation(final ReadWriteTransaction transaction) {
+ final Link link = toTopologyLink(notification);
+ final InstanceIdentifier<Link> path = linkPath(link);
+ transaction.put(LogicalDatastoreType.OPERATIONAL, path, link);
}
});
}
public void onLinkRemoved(final LinkRemoved notification) {
processor.enqueueOperation(new TopologyOperation() {
@Override
- public void applyOperation(final DataModificationTransaction transaction) {
- transaction.removeOperationalData(linkPath(toTopologyLink(notification)));
+ public void applyOperation(final ReadWriteTransaction transaction) {
+ transaction.delete(LogicalDatastoreType.OPERATIONAL, linkPath(toTopologyLink(notification)));
}
});
}
return tpPath(toTopologyNodeId(invNodeKey.getId()), toTerminationPointId(invNodeConnectorKey.getId()));
}
- private void removeAffectedLinks(final DataModificationTransaction transaction, final NodeId id) {
- TypeSafeDataReader reader = TypeSafeDataReader.forReader(transaction);
- Topology topologyData = reader.readOperationalData(topology);
- if (topologyData != null) {
- for (Link link : topologyData.getLink()) {
- if (id.equals(link.getSource().getSourceNode()) || id.equals(link.getDestination().getDestNode())) {
- transaction.removeOperationalData(linkPath(link));
- }
+ private void removeAffectedLinks(final NodeId id) {
+ processor.enqueueOperation(new TopologyOperation() {
+ @Override
+ public void applyOperation(final ReadWriteTransaction transaction) {
+ CheckedFuture<Optional<Topology>, ReadFailedException> topologyDataFuture = transaction.read(LogicalDatastoreType.OPERATIONAL, topology);
+ Futures.addCallback(topologyDataFuture, new FutureCallback<Optional<Topology>>() {
+ @Override
+ public void onSuccess(Optional<Topology> topologyOptional) {
+ if (topologyOptional.isPresent()) {
+ Topology topologyData = topologyOptional.get();
+ for (Link link : topologyData.getLink()) {
+ if (id.equals(link.getSource().getSourceNode()) || id.equals(link.getDestination().getDestNode())) {
+ transaction.delete(LogicalDatastoreType.OPERATIONAL, linkPath(link));
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onFailure(Throwable throwable) {
+ LOG.error("Error reading topology data for topology {}", topology, throwable);
+ }
+ });
}
- }
+ });
}
- private void removeAffectedLinks(final DataModificationTransaction transaction, final TpId id) {
- TypeSafeDataReader reader = TypeSafeDataReader.forReader(transaction);
- Topology topologyData = reader.readOperationalData(topology);
- if (topologyData != null) {
- for (Link link : topologyData.getLink()) {
- if (id.equals(link.getSource().getSourceTp()) || id.equals(link.getDestination().getDestTp())) {
- transaction.removeOperationalData(linkPath(link));
- }
+ private void removeAffectedLinks(final TpId id) {
+ processor.enqueueOperation(new TopologyOperation() {
+ @Override
+ public void applyOperation(final ReadWriteTransaction transaction) {
+ CheckedFuture<Optional<Topology>, ReadFailedException> topologyDataFuture = transaction.read(LogicalDatastoreType.OPERATIONAL, topology);
+ Futures.addCallback(topologyDataFuture, new FutureCallback<Optional<Topology>>() {
+ @Override
+ public void onSuccess(Optional<Topology> topologyOptional) {
+ if (topologyOptional.isPresent()) {
+ Topology topologyData = topologyOptional.get();
+ for (Link link : topologyData.getLink()) {
+ if (id.equals(link.getSource().getSourceTp()) || id.equals(link.getDestination().getDestTp())) {
+ transaction.delete(LogicalDatastoreType.OPERATIONAL, linkPath(link));
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onFailure(Throwable throwable) {
+ LOG.error("Error reading topology data for topology {}", topology, throwable);
+ }
+ });
}
- }
+ });
}
private InstanceIdentifier<Node> getNodePath(final NodeId nodeId) {
import java.util.concurrent.ExecutionException;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
import org.opendaylight.controller.sal.binding.api.AbstractBindingAwareProvider;
import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
-import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
-import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.TopologyId;
import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
*/
@Override
public synchronized void onSessionInitiated(final ProviderContext session) {
- final DataProviderService dataService = session.getSALService(DataProviderService.class);
+ final DataBroker dataBroker = session.getSALService(DataBroker.class);
final NotificationProviderService notificationService = session.getSALService(NotificationProviderService.class);
final String name = "flow:1";
final TopologyKey key = new TopologyKey(new TopologyId(name));
final InstanceIdentifier<Topology> path = InstanceIdentifier
- .builder(NetworkTopology.class)
- .child(Topology.class, key)
- .build();
+ .create(NetworkTopology.class)
+ .child(Topology.class, key);
- final OperationProcessor processor = new OperationProcessor(dataService);
+ final OperationProcessor processor = new OperationProcessor(dataBroker);
final FlowCapableTopologyExporter listener = new FlowCapableTopologyExporter(processor, path);
this.listenerRegistration = notificationService.registerNotificationListener(listener);
- final DataModificationTransaction tx = dataService.beginTransaction();
- tx.putOperationalData(path, new TopologyBuilder().setKey(key).build());
+ final ReadWriteTransaction tx = dataBroker.newReadWriteTransaction();
+ tx.put(LogicalDatastoreType.OPERATIONAL, path, new TopologyBuilder().setKey(key).build(), true);
try {
- tx.commit().get();
+ tx.submit().get();
} catch (InterruptedException | ExecutionException e) {
LOG.warn("Initial topology export failed, continuing anyway", e);
}
/**
* Gets called during stop bundle
*
- * @param context
- * The execution context of the bundle being stopped.
+ * @param context The execution context of the bundle being stopped.
*/
@Override
public void stopImpl(final BundleContext context) {
*/
package org.opendaylight.md.controller.topology.manager;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.ExecutionException;
import java.util.concurrent.LinkedBlockingQueue;
-
-import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
-import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
-import org.opendaylight.controller.sal.binding.api.data.DataProviderService;
-import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.base.Preconditions;
-
final class OperationProcessor implements Runnable {
private static final Logger LOG = LoggerFactory.getLogger(OperationProcessor.class);
private static final int MAX_TRANSACTION_OPERATIONS = 100;
private static final int OPERATION_QUEUE_DEPTH = 500;
private final BlockingQueue<TopologyOperation> queue = new LinkedBlockingQueue<>(OPERATION_QUEUE_DEPTH);
- // FIXME: Flow capable topology exporter should use transaction chaining API
- private final DataProviderService dataService;
+ private final DataBroker dataBroker;
- OperationProcessor(final DataProviderService dataService) {
- this.dataService = Preconditions.checkNotNull(dataService);
+ OperationProcessor(final DataBroker dataBroker) {
+ this.dataBroker = Preconditions.checkNotNull(dataBroker);
}
void enqueueOperation(final TopologyOperation task) {
@Override
public void run() {
try {
- for (;;) {
+ for (; ; ) {
TopologyOperation op = queue.take();
LOG.debug("New operations available, starting transaction");
- final DataModificationTransaction tx = dataService.beginTransaction();
+ final ReadWriteTransaction tx = dataBroker.newReadWriteTransaction();
int ops = 0;
do {
LOG.debug("Processed {} operations, submitting transaction", ops);
- try {
- final RpcResult<TransactionStatus> s = tx.commit().get();
- if (!s.isSuccessful()) {
- LOG.error("Topology export failed for Tx:{}", tx.getIdentifier());
+ final CheckedFuture txResultFuture = tx.submit();
+ Futures.addCallback(txResultFuture, new FutureCallback() {
+ @Override
+ public void onSuccess(Object o) {
+ LOG.debug("Topology export successful for tx :{}", tx.getIdentifier());
+ }
+
+ @Override
+ public void onFailure(Throwable throwable) {
+ LOG.error("Topology export transaction {} failed", tx.getIdentifier(), throwable.getCause());
}
- } catch (ExecutionException e) {
- LOG.error("Topology export transaction {} failed", tx.getIdentifier(), e.getCause());
- }
+ });
}
} catch (InterruptedException e) {
LOG.info("Interrupted processing, terminating", e);
*/
package org.opendaylight.md.controller.topology.manager;
-import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
/**
* Internal interface for submitted operations. Implementations of this
*
* @param transaction Datastore transaction
*/
- void applyOperation(DataModificationTransaction transaction);
+ void applyOperation(ReadWriteTransaction transaction);
}
\ No newline at end of file
import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider;
+import org.opendaylight.controller.netconf.ssh.authentication.AuthProviderImpl;
import org.opendaylight.controller.netconf.ssh.authentication.PEMGenerator;
import org.opendaylight.controller.netconf.util.messages.NetconfMessageUtil;
import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil;
}
public AuthProvider getAuthProvider() throws Exception {
- AuthProvider mock = mock(AuthProvider.class);
+ AuthProvider mock = mock(AuthProviderImpl.class);
doReturn(true).when(mock).authenticated(anyString(), anyString());
doReturn(PEMGenerator.generate().toCharArray()).when(mock).getPEMAsCharArray();
return mock;
/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
+ * Copyright (c) 2014 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.netconf.ssh.authentication;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import com.google.common.annotations.VisibleForTesting;
-import org.opendaylight.controller.sal.authorization.AuthResultEnum;
-import org.opendaylight.controller.usermanager.IUserManager;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.ServiceReference;
-import org.osgi.util.tracker.ServiceTracker;
-import org.osgi.util.tracker.ServiceTrackerCustomizer;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class AuthProvider {
- private static final Logger logger = LoggerFactory.getLogger(AuthProvider.class);
-
- private final String pem;
- private IUserManager nullableUserManager;
- public AuthProvider(String pemCertificate, final BundleContext bundleContext) {
- checkNotNull(pemCertificate, "Parameter 'pemCertificate' is null");
- pem = pemCertificate;
-
- ServiceTrackerCustomizer<IUserManager, IUserManager> customizer = new ServiceTrackerCustomizer<IUserManager, IUserManager>() {
- @Override
- public IUserManager addingService(final ServiceReference<IUserManager> reference) {
- logger.trace("Service {} added", reference);
- nullableUserManager = bundleContext.getService(reference);
- return nullableUserManager;
- }
-
- @Override
- public void modifiedService(final ServiceReference<IUserManager> reference, final IUserManager service) {
- logger.trace("Replacing modified service {} in netconf SSH.", reference);
- nullableUserManager = service;
- }
-
- @Override
- public void removedService(final ServiceReference<IUserManager> reference, final IUserManager service) {
- logger.trace("Removing service {} from netconf SSH. " +
- "SSH won't authenticate users until IUserManager service will be started.", reference);
- synchronized (AuthProvider.this) {
- nullableUserManager = null;
- }
- }
- };
- ServiceTracker<IUserManager, IUserManager> listenerTracker = new ServiceTracker<>(bundleContext, IUserManager.class, customizer);
- listenerTracker.open();
- }
+package org.opendaylight.controller.netconf.ssh.authentication;
- /**
- * Authenticate user. This implementation tracks IUserManager and delegates the decision to it. If the service is not
- * available, IllegalStateException is thrown.
- */
- public synchronized boolean authenticated(String username, String password) {
- if (nullableUserManager == null) {
- logger.warn("Cannot authenticate user '{}', user manager service is missing", username);
- throw new IllegalStateException("User manager service is not available");
- }
- AuthResultEnum authResult = nullableUserManager.authenticate(username, password);
- logger.debug("Authentication result for user '{}' : {}", username, authResult);
- return authResult.equals(AuthResultEnum.AUTH_ACCEPT) || authResult.equals(AuthResultEnum.AUTH_ACCEPT_LOC);
- }
+public interface AuthProvider {
- public char[] getPEMAsCharArray() {
- return pem.toCharArray();
- }
+ boolean authenticated(String username, String password);
- @VisibleForTesting
- void setNullableUserManager(IUserManager nullableUserManager) {
- this.nullableUserManager = nullableUserManager;
- }
+ char[] getPEMAsCharArray();
}
--- /dev/null
+/*
+ * 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.netconf.ssh.authentication;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.annotations.VisibleForTesting;
+import org.opendaylight.controller.sal.authorization.AuthResultEnum;
+import org.opendaylight.controller.usermanager.IUserManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AuthProviderImpl implements AuthProvider {
+ private static final Logger logger = LoggerFactory.getLogger(AuthProviderImpl.class);
+
+ private final String pem;
+ private IUserManager nullableUserManager;
+
+ public AuthProviderImpl(String pemCertificate, final BundleContext bundleContext) {
+ checkNotNull(pemCertificate, "Parameter 'pemCertificate' is null");
+ pem = pemCertificate;
+
+ ServiceTrackerCustomizer<IUserManager, IUserManager> customizer = new ServiceTrackerCustomizer<IUserManager, IUserManager>() {
+ @Override
+ public IUserManager addingService(final ServiceReference<IUserManager> reference) {
+ logger.trace("Service {} added", reference);
+ nullableUserManager = bundleContext.getService(reference);
+ return nullableUserManager;
+ }
+
+ @Override
+ public void modifiedService(final ServiceReference<IUserManager> reference, final IUserManager service) {
+ logger.trace("Replacing modified service {} in netconf SSH.", reference);
+ nullableUserManager = service;
+ }
+
+ @Override
+ public void removedService(final ServiceReference<IUserManager> reference, final IUserManager service) {
+ logger.trace("Removing service {} from netconf SSH. " +
+ "SSH won't authenticate users until IUserManager service will be started.", reference);
+ synchronized (AuthProviderImpl.this) {
+ nullableUserManager = null;
+ }
+ }
+ };
+ ServiceTracker<IUserManager, IUserManager> listenerTracker = new ServiceTracker<>(bundleContext, IUserManager.class, customizer);
+ listenerTracker.open();
+ }
+
+ /**
+ * Authenticate user. This implementation tracks IUserManager and delegates the decision to it. If the service is not
+ * available, IllegalStateException is thrown.
+ */
+ @Override
+ public synchronized boolean authenticated(String username, String password) {
+ if (nullableUserManager == null) {
+ logger.warn("Cannot authenticate user '{}', user manager service is missing", username);
+ throw new IllegalStateException("User manager service is not available");
+ }
+ AuthResultEnum authResult = nullableUserManager.authenticate(username, password);
+ logger.debug("Authentication result for user '{}' : {}", username, authResult);
+ return authResult.equals(AuthResultEnum.AUTH_ACCEPT) || authResult.equals(AuthResultEnum.AUTH_ACCEPT_LOC);
+ }
+
+ @Override
+ public char[] getPEMAsCharArray() {
+ return pem.toCharArray();
+ }
+
+ @VisibleForTesting
+ void setNullableUserManager(IUserManager nullableUserManager) {
+ this.nullableUserManager = nullableUserManager;
+ }
+}
import org.apache.commons.lang3.StringUtils;
import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider;
+import org.opendaylight.controller.netconf.ssh.authentication.AuthProviderImpl;
import org.opendaylight.controller.netconf.ssh.authentication.PEMGenerator;
import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil;
import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil.InfixProp;
checkState(StringUtils.isNotBlank(path), "Path to ssh private key is blank. Reconfigure %s", NetconfConfigUtil.getPrivateKeyKey());
String privateKeyPEMString = PEMGenerator.readOrGeneratePK(new File(path));
- final AuthProvider authProvider = new AuthProvider(privateKeyPEMString, bundleContext);
+ final AuthProvider authProvider = new AuthProviderImpl(privateKeyPEMString, bundleContext);
EventLoopGroup bossGroup = new NioEventLoopGroup();
NetconfSSHServer server = NetconfSSHServer.start(sshSocketAddress.getPort(), localAddress, authProvider, bossGroup);
import org.opendaylight.controller.netconf.nettyutil.handler.ssh.client.SshHandler;
import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider;
+import org.opendaylight.controller.netconf.ssh.authentication.AuthProviderImpl;
import org.opendaylight.controller.netconf.ssh.authentication.PEMGenerator;
import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil;
import org.slf4j.Logger;
@Test
public void test() throws Exception {
new Thread(new EchoServer(), "EchoServer").start();
- AuthProvider authProvider = mock(AuthProvider.class);
+ AuthProvider authProvider = mock(AuthProviderImpl.class);
doReturn(PEMGenerator.generate().toCharArray()).when(authProvider).getPEMAsCharArray();
doReturn(true).when(authProvider).authenticated(anyString(), anyString());
NetconfSSHServer netconfSSHServer = NetconfSSHServer.start(10831, NetconfConfigUtil.getNetconfLocalAddress(),
try (InputStream is = getClass().getResourceAsStream("/RSA.pk")) {
pem = IOUtils.toString(is);
}
- AuthProvider ap = new AuthProvider(pem, mockedContext);
+ AuthProviderImpl ap = new AuthProviderImpl(pem, mockedContext);
ap.setNullableUserManager(um);
EventLoopGroup bossGroup = new NioEventLoopGroup();
NetconfSSHServer server = NetconfSSHServer.start(PORT, NetconfConfigUtil.getNetconfLocalAddress(),
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (c) 2014 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
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-subsystem</artifactId>
+ <version>0.2.5-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>netconf-testtool</artifactId>
+ <name>${project.artifactId}</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>net.sourceforge.argparse4j</groupId>
+ <artifactId>argparse4j</artifactId>
+ <version>0.4.3</version>
+ </dependency>
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-netty-util</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>commons.logback_settings</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>config-netconf-connector</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>netconf-connector-config</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.controller</groupId>
+ <artifactId>logback-config</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>mockito-configuration</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>xmlunit</groupId>
+ <artifactId>xmlunit</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>config-util</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-client</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-impl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-mapping-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-monitoring</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netconf-ssh</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>netty-config-api</artifactId>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-shade-plugin</artifactId>
+ <configuration></configuration>
+ <executions>
+ <execution>
+ <goals>
+ <goal>shade</goal>
+ </goals>
+ <phase>package</phase>
+ <configuration>
+ <!-- TODO investigate why jar fails without this filter-->
+ <filters>
+ <filter>
+ <artifact>*:*</artifact>
+ <excludes>
+ <exclude>META-INF/*.SF</exclude>
+ <exclude>META-INF/*.DSA</exclude>
+ <exclude>META-INF/*.RSA</exclude>
+ </excludes>
+ </filter>
+ </filters>
+ <transformers>
+ <transformer
+ implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+ <mainClass>org.opendaylight.controller.netconf.test.tool.Main</mainClass>
+ </transformer>
+ </transformers>
+ <shadedArtifactAttached>true</shadedArtifactAttached>
+ <shadedClassifierName>executable</shadedClassifierName>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
--- /dev/null
+/*
+ * Copyright (c) 2014 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.netconf.test.tool;
+
+import java.io.File;
+import java.io.IOException;
+import org.opendaylight.controller.netconf.ssh.authentication.AuthProvider;
+import org.opendaylight.controller.netconf.ssh.authentication.PEMGenerator;
+
+class AcceptingAuthProvider implements AuthProvider {
+ private final String privateKeyPEMString;
+
+ public AcceptingAuthProvider() {
+ try {
+ this.privateKeyPEMString = PEMGenerator.readOrGeneratePK(new File("PK"));
+ } catch (final IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public synchronized boolean authenticated(final String username, final String password) {
+ return true;
+ }
+
+ @Override
+ public char[] getPEMAsCharArray() {
+ return privateKeyPEMString.toCharArray();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.netconf.test.tool;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.io.Files;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.List;
+
+import net.sourceforge.argparse4j.ArgumentParsers;
+import net.sourceforge.argparse4j.annotation.Arg;
+import net.sourceforge.argparse4j.inf.ArgumentParser;
+import net.sourceforge.argparse4j.inf.ArgumentParserException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Charsets;
+import com.google.common.io.CharStreams;
+
+public final class Main {
+
+ // TODO add logback config
+
+ // TODO make exi configurable
+
+ private static final Logger LOG = LoggerFactory.getLogger(Main.class);
+
+ static class Params {
+
+ @Arg(dest = "schemas-dir")
+ public File schemasDir;
+
+ @Arg(dest = "devices-count")
+ public int deviceCount;
+
+ @Arg(dest = "starting-port")
+ public int startingPort;
+
+ @Arg(dest = "generate-configs-dir")
+ public File generateConfigsDir;
+
+ @Arg(dest = "generate-configs-batch-size")
+ public int generateConfigBatchSize;
+
+ @Arg(dest = "ssh")
+ public boolean ssh;
+
+ static ArgumentParser getParser() {
+ final ArgumentParser parser = ArgumentParsers.newArgumentParser("netconf testool");
+ parser.addArgument("--devices-count")
+ .type(Integer.class)
+ .setDefault(1)
+ .type(Integer.class)
+ .help("Number of simulated netconf devices to spin")
+ .dest("devices-count");
+
+ parser.addArgument("--schemas-dir")
+ .type(File.class)
+ .required(true)
+ .help("Directory containing yang schemas to describe simulated devices")
+ .dest("schemas-dir");
+
+ parser.addArgument("--starting-port")
+ .type(Integer.class)
+ .setDefault(17830)
+ .help("First port for simulated device. Each other device will have previous+1 port number")
+ .dest("starting-port");
+
+ parser.addArgument("--generate-configs-batch-size")
+ .type(Integer.class)
+ .setDefault(100)
+ .help("Number of connector configs per generated file")
+ .dest("generate-configs-batch-size");
+
+ parser.addArgument("--generate-configs-dir")
+ .type(File.class)
+ .help("Directory where initial config files for ODL distribution should be generated")
+ .dest("generate-configs-dir");
+
+ parser.addArgument("--ssh")
+ .type(Boolean.class)
+ .setDefault(true)
+ .help("Whether to use ssh for transport or just pure tcp")
+ .dest("ssh");
+
+ return parser;
+ }
+
+ void validate() {
+ checkArgument(deviceCount > 0, "Device count has to be > 0");
+ checkArgument(startingPort > 1024, "Starting port has to be > 1024");
+
+ checkArgument(schemasDir.exists(), "Schemas dir has to exist");
+ checkArgument(schemasDir.isDirectory(), "Schemas dir has to be a directory");
+ checkArgument(schemasDir.canRead(), "Schemas dir has to be readable");
+ }
+ }
+
+ public static void main(final String[] args) {
+ final Params params = parseArgs(args, Params.getParser());
+ params.validate();
+
+ final NetconfDeviceSimulator netconfDeviceSimulator = new NetconfDeviceSimulator();
+ try {
+ final List<Integer> openDevices = netconfDeviceSimulator.start(params);
+ if(params.generateConfigsDir != null) {
+ new ConfigGenerator(params.generateConfigsDir, openDevices).generate(params.ssh, params.generateConfigBatchSize);
+ }
+ } catch (final Exception e) {
+ LOG.error("Unhandled exception", e);
+ netconfDeviceSimulator.close();
+ System.exit(1);
+ }
+
+ // Block main thread
+ synchronized (netconfDeviceSimulator) {
+ try {
+ netconfDeviceSimulator.wait();
+ } catch (final InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+
+ private static Params parseArgs(final String[] args, final ArgumentParser parser) {
+ final Params opt = new Params();
+ try {
+ parser.parseArgs(args, opt);
+ return opt;
+ } catch (final ArgumentParserException e) {
+ parser.handleError(e);
+ }
+
+ System.exit(1);
+ return null;
+ }
+
+ private static class ConfigGenerator {
+ public static final String NETCONF_CONNECTOR_XML = "/initial/99-netconf-connector.xml";
+ public static final String NETCONF_CONNECTOR_NAME = "controller-config";
+ public static final String NETCONF_CONNECTOR_PORT = "1830";
+ public static final String NETCONF_USE_SSH = "false";
+ public static final String SIM_DEVICE_SUFFIX = "-sim-device";
+
+ private final File directory;
+ private final List<Integer> openDevices;
+
+ public ConfigGenerator(final File directory, final List<Integer> openDevices) {
+ this.directory = directory;
+ this.openDevices = openDevices;
+ }
+
+ public void generate(final boolean useSsh, final int batchSize) {
+ if(directory.exists() == false) {
+ checkState(directory.mkdirs(), "Unable to create folder %s" + directory);
+ }
+
+ try(InputStream stream = Main.class.getResourceAsStream(NETCONF_CONNECTOR_XML)) {
+ checkNotNull(stream, "Cannot load %s", NETCONF_CONNECTOR_XML);
+ String configBlueprint = CharStreams.toString(new InputStreamReader(stream, Charsets.UTF_8));
+
+ // TODO make address configurable
+ checkState(configBlueprint.contains(NETCONF_CONNECTOR_NAME));
+ checkState(configBlueprint.contains(NETCONF_CONNECTOR_PORT));
+ checkState(configBlueprint.contains(NETCONF_USE_SSH));
+ configBlueprint = configBlueprint.replace(NETCONF_CONNECTOR_NAME, "%s");
+ configBlueprint = configBlueprint.replace(NETCONF_CONNECTOR_PORT, "%s");
+ configBlueprint = configBlueprint.replace(NETCONF_USE_SSH, "%s");
+
+ final String before = configBlueprint.substring(0, configBlueprint.indexOf("<module>"));
+ final String middleBlueprint = configBlueprint.substring(configBlueprint.indexOf("<module>"), configBlueprint.indexOf("</module>") + "</module>".length());
+ final String after = configBlueprint.substring(configBlueprint.indexOf("</module>") + "</module>".length());
+
+ int connectorCount = 0;
+ Integer batchStart = null;
+ StringBuilder b = new StringBuilder();
+ b.append(before);
+
+ for (final Integer openDevice : openDevices) {
+ if(batchStart == null) {
+ batchStart = openDevice;
+ }
+
+ final String name = String.valueOf(openDevice) + SIM_DEVICE_SUFFIX;
+ final String configContent = String.format(middleBlueprint, name, String.valueOf(openDevice), String.valueOf(!useSsh));
+ b.append(configContent);
+ connectorCount++;
+ if(connectorCount == batchSize) {
+ b.append(after);
+ Files.write(b.toString(), new File(directory, String.format("simulated-devices_%d-%d.xml", batchStart, openDevice)), Charsets.UTF_8);
+ connectorCount = 0;
+ b = new StringBuilder();
+ b.append(before);
+ batchStart = null;
+ }
+ }
+
+ // Write remaining
+ if(connectorCount != 0) {
+ b.append(after);
+ Files.write(b.toString(), new File(directory, String.format("simulated-devices_%d-%d.xml", batchStart, openDevices.get(openDevices.size() - 1))), Charsets.UTF_8);
+ }
+
+ LOG.info("Config files generated in {}", directory);
+ } catch (final IOException e) {
+ throw new RuntimeException("Unable to generate config files", e);
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.netconf.test.tool;
+
+import com.google.common.base.Optional;
+import java.util.Date;
+import java.util.List;
+import org.opendaylight.controller.netconf.confignetconfconnector.util.Util;
+import org.opendaylight.controller.netconf.mapping.api.Capability;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleBuilder;
+
+final class ModuleBuilderCapability implements Capability {
+ private static final Date NO_REVISION = new Date(0);
+ private final ModuleBuilder input;
+ private final Optional<String> content;
+
+ public ModuleBuilderCapability(final ModuleBuilder input, final String inputStream) {
+ this.input = input;
+ this.content = Optional.of(inputStream);
+ }
+
+ @Override
+ public String getCapabilityUri() {
+ // FIXME capabilities in Netconf-impl need to check for NO REVISION
+ final String withoutRevision = getModuleNamespace().get() + "?module=" + getModuleName().get();
+ return hasRevision() ? withoutRevision + "&revision=" + Util.writeDate(input.getRevision()) : withoutRevision;
+ }
+
+ @Override
+ public Optional<String> getModuleNamespace() {
+ return Optional.of(input.getNamespace().toString());
+ }
+
+ @Override
+ public Optional<String> getModuleName() {
+ return Optional.of(input.getName());
+ }
+
+ @Override
+ public Optional<String> getRevision() {
+ return Optional.of(hasRevision() ? QName.formattedRevision(input.getRevision()) : "");
+ }
+
+ private boolean hasRevision() {
+ return !input.getRevision().equals(NO_REVISION);
+ }
+
+ @Override
+ public Optional<String> getCapabilitySchema() {
+ return content;
+ }
+
+ @Override
+ public Optional<List<String>> getLocation() {
+ return Optional.absent();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.netconf.test.tool;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Function;
+import com.google.common.base.Optional;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.google.common.io.CharStreams;
+import com.google.common.util.concurrent.CheckedFuture;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.local.LocalAddress;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.util.HashedWheelTimer;
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.lang.management.ManagementFactory;
+import java.net.Inet4Address;
+import java.net.InetSocketAddress;
+import java.net.URI;
+import java.net.UnknownHostException;
+import java.util.AbstractMap;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.concurrent.ExecutionException;
+import org.antlr.v4.runtime.ParserRuleContext;
+import org.antlr.v4.runtime.tree.ParseTreeWalker;
+import org.opendaylight.controller.netconf.api.monitoring.NetconfManagementSession;
+import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer;
+import org.opendaylight.controller.netconf.impl.NetconfServerDispatcher;
+import org.opendaylight.controller.netconf.impl.NetconfServerSessionNegotiatorFactory;
+import org.opendaylight.controller.netconf.impl.SessionIdProvider;
+import org.opendaylight.controller.netconf.impl.osgi.NetconfMonitoringServiceImpl;
+import org.opendaylight.controller.netconf.impl.osgi.SessionMonitoringService;
+import org.opendaylight.controller.netconf.mapping.api.Capability;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperation;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationProvider;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService;
+import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceSnapshot;
+import org.opendaylight.controller.netconf.monitoring.osgi.NetconfMonitoringOperationService;
+import org.opendaylight.controller.netconf.ssh.NetconfSSHServer;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
+import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation;
+import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
+import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceListener;
+import org.opendaylight.yangtools.yang.model.repo.util.FilesystemSchemaSourceCache;
+import org.opendaylight.yangtools.yang.parser.builder.impl.BuilderUtils;
+import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleBuilder;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserListenerImpl;
+import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository;
+import org.opendaylight.yangtools.yang.parser.util.ASTSchemaSource;
+import org.opendaylight.yangtools.yang.parser.util.TextToASTTransformer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class NetconfDeviceSimulator implements Closeable {
+
+ private static final Logger LOG = LoggerFactory.getLogger(NetconfDeviceSimulator.class);
+
+ public static final int CONNECTION_TIMEOUT_MILLIS = 20000;
+
+ private final NioEventLoopGroup nettyThreadgroup;
+ private final HashedWheelTimer hashedWheelTimer;
+ private final List<Channel> devicesChannels = Lists.newArrayList();
+
+ public NetconfDeviceSimulator() {
+ this(new NioEventLoopGroup(), new HashedWheelTimer());
+ }
+
+ public NetconfDeviceSimulator(final NioEventLoopGroup eventExecutors, final HashedWheelTimer hashedWheelTimer) {
+ this.nettyThreadgroup = eventExecutors;
+ this.hashedWheelTimer = hashedWheelTimer;
+ }
+
+ private NetconfServerDispatcher createDispatcher(final Map<ModuleBuilder, String> moduleBuilders) {
+
+ final Set<Capability> capabilities = Sets.newHashSet(Collections2.transform(moduleBuilders.keySet(), new Function<ModuleBuilder, Capability>() {
+ @Override
+ public Capability apply(final ModuleBuilder input) {
+ return new ModuleBuilderCapability(input, moduleBuilders.get(input));
+ }
+ }));
+
+ final SessionIdProvider idProvider = new SessionIdProvider();
+
+ final SimulatedOperationProvider simulatedOperationProvider = new SimulatedOperationProvider(idProvider, capabilities);
+ final NetconfMonitoringOperationService monitoringService = new NetconfMonitoringOperationService(new NetconfMonitoringServiceImpl(simulatedOperationProvider));
+ simulatedOperationProvider.addService(monitoringService);
+
+ final DefaultCommitNotificationProducer commitNotifier = new DefaultCommitNotificationProducer(ManagementFactory.getPlatformMBeanServer());
+
+ final NetconfServerSessionNegotiatorFactory serverNegotiatorFactory = new NetconfServerSessionNegotiatorFactory(
+ hashedWheelTimer, simulatedOperationProvider, idProvider, CONNECTION_TIMEOUT_MILLIS, commitNotifier, new LoggingMonitoringService());
+
+ final NetconfServerDispatcher.ServerChannelInitializer serverChannelInitializer = new NetconfServerDispatcher.ServerChannelInitializer(
+ serverNegotiatorFactory);
+ return new NetconfServerDispatcher(serverChannelInitializer, nettyThreadgroup, nettyThreadgroup);
+ }
+
+ private Map<ModuleBuilder, String> toModuleBuilders(final Map<SourceIdentifier, Map.Entry<ASTSchemaSource, YangTextSchemaSource>> sources) {
+ final Map<SourceIdentifier, ParserRuleContext> asts = Maps.transformValues(sources, new Function<Map.Entry<ASTSchemaSource, YangTextSchemaSource>, ParserRuleContext>() {
+ @Override
+ public ParserRuleContext apply(final Map.Entry<ASTSchemaSource, YangTextSchemaSource> input) {
+ return input.getKey().getAST();
+ }
+ });
+ final Map<String, TreeMap<Date, URI>> namespaceContext = BuilderUtils.createYangNamespaceContext(
+ asts.values(), Optional.<SchemaContext>absent());
+
+ final ParseTreeWalker walker = new ParseTreeWalker();
+ final Map<ModuleBuilder, String> sourceToBuilder = new HashMap<>();
+
+ for (final Map.Entry<SourceIdentifier, ParserRuleContext> entry : asts.entrySet()) {
+ final ModuleBuilder moduleBuilder = YangParserListenerImpl.create(namespaceContext, entry.getKey().getName(),
+ walker, entry.getValue()).getModuleBuilder();
+
+ try(InputStreamReader stream = new InputStreamReader(sources.get(entry.getKey()).getValue().openStream(), Charsets.UTF_8)) {
+ sourceToBuilder.put(moduleBuilder, CharStreams.toString(stream));
+ } catch (final IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ return sourceToBuilder;
+ }
+
+
+ public List<Integer> start(final Main.Params params) {
+ final Map<ModuleBuilder, String> moduleBuilders = parseSchemasToModuleBuilders(params);
+
+ final NetconfServerDispatcher dispatcher = createDispatcher(moduleBuilders);
+
+ int currentPort = params.startingPort;
+
+ final List<Integer> openDevices = Lists.newArrayList();
+ for (int i = 0; i < params.deviceCount; i++) {
+ final InetSocketAddress address = getAddress(currentPort);
+
+ final ChannelFuture server;
+ if(params.ssh) {
+ final LocalAddress tcpLocalAddress = new LocalAddress(address.toString());
+
+ server = dispatcher.createLocalServer(tcpLocalAddress);
+ try {
+ NetconfSSHServer.start(currentPort, tcpLocalAddress, new AcceptingAuthProvider(), nettyThreadgroup);
+ } catch (final Exception e) {
+ LOG.warn("Cannot start simulated device on {}, skipping", address, e);
+ // Close local server and continue
+ server.cancel(true);
+ if(server.isDone()) {
+ server.channel().close();
+ }
+ continue;
+ } finally {
+ currentPort++;
+ }
+
+ try {
+ server.get();
+ } catch (final InterruptedException e) {
+ throw new RuntimeException(e);
+ } catch (final ExecutionException e) {
+ LOG.warn("Cannot start ssh simulated device on {}, skipping", address, e);
+ continue;
+ }
+
+ LOG.debug("Simulated SSH device started on {}", address);
+
+ } else {
+ server = dispatcher.createServer(address);
+ currentPort++;
+
+ try {
+ server.get();
+ } catch (final InterruptedException e) {
+ throw new RuntimeException(e);
+ } catch (final ExecutionException e) {
+ LOG.warn("Cannot start tcp simulated device on {}, skipping", address, e);
+ continue;
+ }
+
+ LOG.debug("Simulated TCP device started on {}", address);
+ }
+
+ devicesChannels.add(server.channel());
+ openDevices.add(currentPort - 1);
+
+ }
+
+ if(openDevices.size() == params.deviceCount) {
+ LOG.info("All simulated devices started successfully from port {} to {}", params.startingPort, currentPort);
+ } else {
+ LOG.warn("Not all simulated devices started successfully. Started devices ar on ports {}", openDevices);
+ }
+
+ return openDevices;
+ }
+
+ private Map<ModuleBuilder, String> parseSchemasToModuleBuilders(final Main.Params params) {
+ final SharedSchemaRepository consumer = new SharedSchemaRepository("netconf-simulator");
+ consumer.registerSchemaSourceListener(TextToASTTransformer.create(consumer, consumer));
+
+ final Set<SourceIdentifier> loadedSources = Sets.newHashSet();
+
+ consumer.registerSchemaSourceListener(new SchemaSourceListener() {
+ @Override
+ public void schemaSourceEncountered(final SchemaSourceRepresentation schemaSourceRepresentation) {}
+
+ @Override
+ public void schemaSourceRegistered(final Iterable<PotentialSchemaSource<?>> potentialSchemaSources) {
+ for (final PotentialSchemaSource<?> potentialSchemaSource : potentialSchemaSources) {
+ loadedSources.add(potentialSchemaSource.getSourceIdentifier());
+ }
+ }
+
+ @Override
+ public void schemaSourceUnregistered(final PotentialSchemaSource<?> potentialSchemaSource) {}
+ });
+
+ final FilesystemSchemaSourceCache<YangTextSchemaSource> cache = new FilesystemSchemaSourceCache<>(consumer, YangTextSchemaSource.class, params.schemasDir);
+ consumer.registerSchemaSourceListener(cache);
+
+ final Map<SourceIdentifier, Map.Entry<ASTSchemaSource, YangTextSchemaSource>> asts = Maps.newHashMap();
+ for (final SourceIdentifier loadedSource : loadedSources) {
+ try {
+ final CheckedFuture<ASTSchemaSource, SchemaSourceException> ast = consumer.getSchemaSource(loadedSource, ASTSchemaSource.class);
+ final CheckedFuture<YangTextSchemaSource, SchemaSourceException> text = consumer.getSchemaSource(loadedSource, YangTextSchemaSource.class);
+ asts.put(loadedSource, new AbstractMap.SimpleEntry<>(ast.get(), text.get()));
+ } catch (final InterruptedException e) {
+ throw new RuntimeException(e);
+ } catch (final ExecutionException e) {
+ throw new RuntimeException("Cannot parse schema context", e);
+ }
+ }
+ return toModuleBuilders(asts);
+ }
+
+ private static InetSocketAddress getAddress(final int port) {
+ try {
+ // TODO make address configurable
+ return new InetSocketAddress(Inet4Address.getByName("0.0.0.0"), port);
+ } catch (final UnknownHostException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void close() {
+ for (final Channel deviceCh : devicesChannels) {
+ deviceCh.close();
+ }
+ nettyThreadgroup.shutdownGracefully();
+ // close Everything
+ }
+
+ private static class SimulatedOperationProvider implements NetconfOperationProvider {
+ private final SessionIdProvider idProvider;
+ private final Set<NetconfOperationService> netconfOperationServices;
+
+
+ public SimulatedOperationProvider(final SessionIdProvider idProvider, final Set<Capability> caps) {
+ this.idProvider = idProvider;
+ final SimulatedOperationService simulatedOperationService = new SimulatedOperationService(caps, idProvider.getCurrentSessionId());
+ this.netconfOperationServices = Sets.<NetconfOperationService>newHashSet(simulatedOperationService);
+ }
+
+ @Override
+ public NetconfOperationServiceSnapshot openSnapshot(final String sessionIdForReporting) {
+ return new SimulatedServiceSnapshot(idProvider, netconfOperationServices);
+ }
+
+ public void addService(final NetconfOperationService monitoringService) {
+ netconfOperationServices.add(monitoringService);
+ }
+
+ private static class SimulatedServiceSnapshot implements NetconfOperationServiceSnapshot {
+ private final SessionIdProvider idProvider;
+ private final Set<NetconfOperationService> netconfOperationServices;
+
+ public SimulatedServiceSnapshot(final SessionIdProvider idProvider, final Set<NetconfOperationService> netconfOperationServices) {
+ this.idProvider = idProvider;
+ this.netconfOperationServices = netconfOperationServices;
+ }
+
+ @Override
+ public String getNetconfSessionIdForReporting() {
+ return String.valueOf(idProvider.getCurrentSessionId());
+ }
+
+ @Override
+ public Set<NetconfOperationService> getServices() {
+ return netconfOperationServices;
+ }
+
+ @Override
+ public void close() throws Exception {}
+ }
+
+ static class SimulatedOperationService implements NetconfOperationService {
+ private final Set<Capability> capabilities;
+ private static SimulatedGet sGet;
+
+ public SimulatedOperationService(final Set<Capability> capabilities, final long currentSessionId) {
+ this.capabilities = capabilities;
+ sGet = new SimulatedGet(String.valueOf(currentSessionId));
+ }
+
+ @Override
+ public Set<Capability> getCapabilities() {
+ return capabilities;
+ }
+
+ @Override
+ public Set<NetconfOperation> getNetconfOperations() {
+ return Sets.<NetconfOperation>newHashSet(sGet);
+ }
+
+ @Override
+ public void close() {
+ }
+
+ }
+ }
+
+ private class LoggingMonitoringService implements SessionMonitoringService {
+ @Override
+ public void onSessionUp(final NetconfManagementSession session) {
+ LOG.debug("Session {} established", session);
+ }
+
+ @Override
+ public void onSessionDown(final NetconfManagementSession session) {
+ LOG.debug("Session {} down", session);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.netconf.test.tool;
+
+import com.google.common.base.Optional;
+import org.opendaylight.controller.netconf.api.NetconfDocumentedException;
+import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
+import org.opendaylight.controller.netconf.confignetconfconnector.operations.AbstractConfigNetconfOperation;
+import org.opendaylight.controller.netconf.util.xml.XmlElement;
+import org.opendaylight.controller.netconf.util.xml.XmlUtil;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+class SimulatedGet extends AbstractConfigNetconfOperation {
+
+ SimulatedGet(final String netconfSessionIdForReporting) {
+ super(null, netconfSessionIdForReporting);
+ }
+
+ @Override
+ protected Element handleWithNoSubsequentOperations(final Document document, final XmlElement operationElement) throws NetconfDocumentedException {
+ return XmlUtil.createElement(document, XmlNetconfConstants.DATA_KEY, Optional.<String>absent());
+ }
+
+ @Override
+ protected String getOperationName() {
+ return XmlNetconfConstants.GET;
+ }
+}
final NetconfDocumentedException sendErrorException) {
try {
final Element incommingRpc = incommingDocument.getDocumentElement();
- Preconditions.checkState(incommingRpc.getTagName().equals(XmlNetconfConstants.RPC_KEY), "Missing "
- + XmlNetconfConstants.RPC_KEY + " " + "element");
+ Preconditions.checkState(incommingRpc.getTagName().equals(XmlNetconfConstants.RPC_KEY), "Missing %s element",
+ XmlNetconfConstants.RPC_KEY);
final Element rpcReply = errorDocument.getDocumentElement();
- Preconditions.checkState(rpcReply.getTagName().equals(XmlNetconfConstants.RPC_REPLY_KEY), "Missing "
- + XmlNetconfConstants.RPC_REPLY_KEY + " element");
+ Preconditions.checkState(rpcReply.getTagName().equals(XmlNetconfConstants.RPC_REPLY_KEY), "Missing %s element",
+ XmlNetconfConstants.RPC_REPLY_KEY);
final NamedNodeMap incomingAttributes = incommingRpc.getAttributes();
for (int i = 0; i < incomingAttributes.getLength(); i++) {
@Override
public void operationComplete(final ChannelFuture channelFuture) throws Exception {
- Preconditions.checkState(channelFuture.isSuccess(), "Unable to send exception {}", sendErrorException,
+ Preconditions.checkState(channelFuture.isSuccess(), "Unable to send exception %s", sendErrorException,
channelFuture.cause());
}
}
<module>netconf-it</module>
</modules>
</profile>
+
+ <profile>
+ <id>testtool</id>
+ <activation>
+ <activeByDefault>false</activeByDefault>
+ </activation>
+ <modules>
+ <module>netconf-testtool</module>
+ </modules>
+ </profile>
</profiles>
</project>
@XmlElement (name="tenant_id")
String tenantID;
- @XmlElement (name="external_gateway_info")
+ @XmlElement (name="external_gateway_info", nillable=true)
NeutronRouter_NetworkReference externalGatewayInfo;
+ @XmlElement (name="distributed")
+ Boolean distributed;
+
+ @XmlElement (name="gw_port_id", nillable=true)
+ String gatewayPortId;
+
+ @XmlElement (name="routes")
+ List<String> routes;
+
/* Holds a map of OpenStackRouterInterfaces by subnet UUID
* used for internal mapping to DOVE
*/
this.externalGatewayInfo = externalGatewayInfo;
}
+ public Boolean getDistributed() {
+ return distributed;
+ }
+
+ public void setDistributed(Boolean distributed) {
+ this.distributed = distributed;
+ }
+
+ public String getGatewayPortId() {
+ return gatewayPortId;
+ }
+
+ public void setGatewayPortId(String gatewayPortId) {
+ this.gatewayPortId = gatewayPortId;
+ }
+
+ public List<String> getRoutes() {
+ return routes;
+ }
+
+ public void setRoutes(List<String> routes) {
+ this.routes = routes;
+ }
+
/**
* This method copies selected fields from the object and returns them
* as a new object, suitable for marshaling.
* @return an OpenStackRouters object with only the selected fields
* populated
*/
-
public NeutronRouter extractFields(List<String> fields) {
NeutronRouter ans = new NeutronRouter();
Iterator<String> i = fields.iterator();
if (s.equals("external_gateway_info")) {
ans.setExternalGatewayInfo(this.getExternalGatewayInfo());
}
+ if (s.equals("distributed")) {
+ ans.setDistributed(this.getDistributed());
+ }
+ if (s.equals("gw_port_id")) {
+ ans.setGatewayPortId(this.getGatewayPortId());
+ }
+ if (s.equals("routes")){
+ ans.setRoutes(this.getRoutes());
+ }
}
return ans;
}
// if floating IP is specified, make sure it can come from the network
String floatingIP = singleton.getFloatingIPAddress();
if (floatingIP != null) {
- if (externNetwork.getSubnets().size() > 1)
+ if (externNetwork.getSubnets().size() != 1)
throw new BadRequestException("external network doesn't have a subnet");
NeutronSubnet externSubnet = subnetInterface.getSubnet(externNetwork.getSubnets().get(0));
if (!externSubnet.isValidIP(floatingIP))
if (!input.isSingleton())
throw new BadRequestException("only singleton requests allowed.");
NeutronFloatingIP singleton = input.getSingleton();
- if (singleton.getID() != null)
+ if (singleton.getID() == null)
throw new BadRequestException("singleton UUID doesn't exist.");
NeutronNetwork externNetwork = networkInterface.getNetwork(
// if floating IP is specified, make sure it can come from the network
String floatingIP = singleton.getFloatingIPAddress();
if (floatingIP != null) {
- if (externNetwork.getSubnets().size() > 1)
+ if (externNetwork.getSubnets().size() != 1)
throw new BadRequestException("external network doesn't have a subnet.");
NeutronSubnet externSubnet = subnetInterface.getSubnet(externNetwork.getSubnets().get(0));
if (!externSubnet.isValidIP(floatingIP))
<!-- Karaf Distribution -->
<module>opendaylight/dummy-console</module>
<module>opendaylight/karaf-branding</module>
+ <module>opendaylight/distribution/opendaylight-karaf-empty</module>
<module>opendaylight/distribution/opendaylight-karaf</module>
+ <module>opendaylight/distribution/opendaylight-karaf-resources</module>
<module>features</module>
</modules>
<scm>