From: Tony Tkacik Date: Mon, 31 Mar 2014 07:20:57 +0000 (+0000) Subject: Merge "Bug 564 - add missing sal-remote dependency." X-Git-Tag: autorelease-tag-v20140601202136_82eb3f9~300 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=2a4c88aa665a45c5394642cb3604603bebf8c0da;hp=c5df7a9911ba2d77349ec1c7733a9086d99791f6 Merge "Bug 564 - add missing sal-remote dependency." --- diff --git a/opendaylight/commons/opendaylight/pom.xml b/opendaylight/commons/opendaylight/pom.xml index e05059b4ae..e7c7bbe637 100644 --- a/opendaylight/commons/opendaylight/pom.xml +++ b/opendaylight/commons/opendaylight/pom.xml @@ -91,7 +91,7 @@ 0.1.2-SNAPSHOT 0.5.2-SNAPSHOT 0.5.0-SNAPSHOT - 4.0.10.Final + 4.0.17.Final 2.4 0.4.2-SNAPSHOT 0.4.2-SNAPSHOT diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/osgi/ConfigManagerActivator.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/osgi/ConfigManagerActivator.java index 308b137403..6381836af8 100644 --- a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/osgi/ConfigManagerActivator.java +++ b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/osgi/ConfigManagerActivator.java @@ -14,9 +14,7 @@ import org.opendaylight.controller.config.manager.impl.osgi.mapping.ModuleInfoBu import org.opendaylight.controller.config.manager.impl.osgi.mapping.RefreshingSCPModuleInfoRegistry; import org.opendaylight.controller.config.manager.impl.util.OsgiRegistrationUtil; import org.opendaylight.controller.config.spi.ModuleFactory; -import org.opendaylight.yangtools.concepts.ObjectRegistration; import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext; -import org.opendaylight.yangtools.yang.binding.YangModuleInfo; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.util.tracker.ServiceTracker; @@ -25,7 +23,6 @@ import javax.management.InstanceAlreadyExistsException; import javax.management.MBeanServer; import java.lang.management.ManagementFactory; import java.util.Arrays; -import java.util.Collection; import java.util.List; import static org.opendaylight.controller.config.manager.impl.util.OsgiRegistrationUtil.registerService; @@ -57,11 +54,12 @@ public class ConfigManagerActivator implements BundleActivator { // track bundles containing factories BlankTransactionServiceTracker blankTransactionServiceTracker = new BlankTransactionServiceTracker( configRegistry); - ModuleFactoryBundleTracker moduleFactoryBundleTracker = new ModuleFactoryBundleTracker( + ModuleFactoryBundleTracker primaryModuleFactoryBundleTracker = new ModuleFactoryBundleTracker( blankTransactionServiceTracker); // start extensible tracker - ExtensibleBundleTracker>> bundleTracker = new ExtensibleBundleTracker<>(context, moduleInfoBundleTracker, moduleFactoryBundleTracker); + ExtensibleBundleTracker bundleTracker = new ExtensibleBundleTracker<>(context, + primaryModuleFactoryBundleTracker, moduleInfoBundleTracker); bundleTracker.open(); // register config registry to OSGi diff --git a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/osgi/mapping/ModuleInfoBundleTracker.java b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/osgi/mapping/ModuleInfoBundleTracker.java index 8e93583e04..48fdd8855d 100644 --- a/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/osgi/mapping/ModuleInfoBundleTracker.java +++ b/opendaylight/config/config-manager/src/main/java/org/opendaylight/controller/config/manager/impl/osgi/mapping/ModuleInfoBundleTracker.java @@ -7,9 +7,16 @@ */ package org.opendaylight.controller.config.manager.impl.osgi.mapping; +import static java.lang.String.format; + +import java.io.InputStream; +import java.net.URL; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; + import org.apache.commons.io.IOUtils; import org.opendaylight.yangtools.concepts.ObjectRegistration; -import org.opendaylight.yangtools.concepts.Registration; import org.opendaylight.yangtools.sal.binding.generator.api.ModuleInfoRegistry; import org.opendaylight.yangtools.yang.binding.YangModelBindingProvider; import org.opendaylight.yangtools.yang.binding.YangModuleInfo; @@ -19,14 +26,6 @@ import org.osgi.util.tracker.BundleTrackerCustomizer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.InputStream; -import java.net.URL; -import java.util.Collection; -import java.util.LinkedList; -import java.util.List; - -import static java.lang.String.format; - /** * Tracks bundles and attempts to retrieve YangModuleInfo, which is then fed into ModuleInfoRegistry */ @@ -78,7 +77,7 @@ public final class ModuleInfoBundleTracker implements BundleTrackerCustomizer reg : regs) { + for (ObjectRegistration reg : regs) { try { reg.close(); } catch (Exception e) { diff --git a/opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini b/opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini index 0d223b8df2..4598bac593 100644 --- a/opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini +++ b/opendaylight/distribution/opendaylight/src/main/resources/configuration/config.ini @@ -49,7 +49,7 @@ netconf.config.persister.2.properties.numberOfBackups=1 # Set Default start level for framework osgi.bundles.defaultStartLevel=4 # Extra packages to import from the boot class loader -org.osgi.framework.system.packages.extra=sun.reflect,sun.reflect.misc,sun.misc +org.osgi.framework.system.packages.extra=sun.reflect,sun.reflect.misc,sun.misc,sun.nio.ch # This is not Eclipse App eclipse.ignoreApp=true # Don't shutdown equinox if the eclipse App has ended, diff --git a/opendaylight/forwardingrulesmanager/api/src/main/java/org/opendaylight/controller/forwardingrulesmanager/FlowConfig.java b/opendaylight/forwardingrulesmanager/api/src/main/java/org/opendaylight/controller/forwardingrulesmanager/FlowConfig.java index 0841485cb1..c89cfc1b9e 100644 --- a/opendaylight/forwardingrulesmanager/api/src/main/java/org/opendaylight/controller/forwardingrulesmanager/FlowConfig.java +++ b/opendaylight/forwardingrulesmanager/api/src/main/java/org/opendaylight/controller/forwardingrulesmanager/FlowConfig.java @@ -792,6 +792,27 @@ public class FlowConfig extends ConfigurationObject implements Serializable { continue; } + sstr = Pattern.compile(ActionType.ENQUEUE + "=(.*)").matcher(actiongrp); + if (sstr.matches()) { + for (String t : sstr.group(1).split(",")) { + if (t != null) { + String parts[] = t.split(":"); + String nc = String.format("%s|%s@%s", node.getType(), parts[0], node.toString()); + if (NodeConnector.fromString(nc) == null) { + return new Status(StatusCode.BADREQUEST, String.format("Enqueue port is not valid")); + } + if (parts.length > 1) { + try { + Integer.parseInt(parts[1]); + } catch (NumberFormatException e) { + return new Status(StatusCode.BADREQUEST, String.format("Enqueue %s is not in the range 0 - 2147483647", parts[1])); + } + } + } + } + continue; + } + sstr = Pattern.compile(ActionType.SET_VLAN_PCP.toString() + "=(.*)").matcher(actiongrp); if (sstr.matches()) { if ((sstr.group(1) != null) && !isVlanPriorityValid(sstr.group(1))) { diff --git a/opendaylight/md-sal/model/model-flow-statistics/src/main/yang/opendaylight-statistics-types.yang b/opendaylight/md-sal/model/model-flow-statistics/src/main/yang/opendaylight-statistics-types.yang index c4cccc1102..19d6eafa78 100644 --- a/opendaylight/md-sal/model/model-flow-statistics/src/main/yang/opendaylight-statistics-types.yang +++ b/opendaylight/md-sal/model/model-flow-statistics/src/main/yang/opendaylight-statistics-types.yang @@ -2,7 +2,7 @@ module opendaylight-statistics-types { namespace "urn:opendaylight:model:statistics:types"; prefix stat-types; - import ietf-yang-types {prefix yang;} + import ietf-yang-types {prefix yang; revision-date "2010-09-24";} revision "2013-09-25" { description "Initial revision of flow service"; diff --git a/opendaylight/md-sal/sal-binding-api/pom.xml b/opendaylight/md-sal/sal-binding-api/pom.xml index f90031b205..00129b9841 100644 --- a/opendaylight/md-sal/sal-binding-api/pom.xml +++ b/opendaylight/md-sal/sal-binding-api/pom.xml @@ -15,6 +15,10 @@ + + org.opendaylight.yangtools + concepts + org.opendaylight.yangtools yang-common @@ -25,12 +29,11 @@ org.opendaylight.controller - sal-common + sal-common-api - - org.opendaylight.controller - sal-common-api + com.google.guava + guava org.osgi diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataBroker.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataBroker.java new file mode 100644 index 0000000000..c6a9efe21c --- /dev/null +++ b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataBroker.java @@ -0,0 +1,29 @@ +/* + * 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.md.sal.binding.api; + +import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public interface BindingDataBroker extends AsyncDataBroker, DataObject, BindingDataChangeListener>{ + @Override + BindingDataReadTransaction newReadOnlyTransaction(); + + @Override + BindingDataReadWriteTransaction newReadWriteTransaction(); + + @Override + BindingDataWriteTransaction newWriteOnlyTransaction(); + + @Override + ListenerRegistration registerDataChangeListener(LogicalDatastoreType store, + InstanceIdentifier path, BindingDataChangeListener listener, DataChangeScope triggeringScope); +} diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataChangeListener.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataChangeListener.java new file mode 100644 index 0000000000..94ac2d21a3 --- /dev/null +++ b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataChangeListener.java @@ -0,0 +1,18 @@ +/* + * 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.md.sal.binding.api; + +import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent; +import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public interface BindingDataChangeListener extends AsyncDataChangeListener, DataObject> { + @Override + void onDataChanged(AsyncDataChangeEvent, DataObject> change); +} diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataReadTransaction.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataReadTransaction.java new file mode 100644 index 0000000000..93df3eb1e2 --- /dev/null +++ b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataReadTransaction.java @@ -0,0 +1,21 @@ +/* + * 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.md.sal.binding.api; + +import org.opendaylight.controller.md.sal.common.api.data.AsyncReadTransaction; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +import com.google.common.base.Optional; +import com.google.common.util.concurrent.ListenableFuture; + +public interface BindingDataReadTransaction extends AsyncReadTransaction, DataObject> { + @Override + ListenableFuture> read(LogicalDatastoreType store, InstanceIdentifier path); +} diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataReadWriteTransaction.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataReadWriteTransaction.java new file mode 100644 index 0000000000..0dcf020c37 --- /dev/null +++ b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataReadWriteTransaction.java @@ -0,0 +1,20 @@ +/* + * 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.md.sal.binding.api; + +import org.opendaylight.controller.md.sal.common.api.data.AsyncReadWriteTransaction; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Logical capture of a combination of both {@link BindingDataReadTransaction} and + * {@link BindingDataWriteTransaction}. + */ +public interface BindingDataReadWriteTransaction extends BindingDataReadTransaction, BindingDataWriteTransaction, AsyncReadWriteTransaction, DataObject> { + +} diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataWriteTransaction.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataWriteTransaction.java new file mode 100644 index 0000000000..e989f73f39 --- /dev/null +++ b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataWriteTransaction.java @@ -0,0 +1,21 @@ +/* + * 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.md.sal.binding.api; + +import org.opendaylight.controller.md.sal.common.api.data.AsyncWriteTransaction; +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public interface BindingDataWriteTransaction extends AsyncWriteTransaction, DataObject> { + @Override + void put(LogicalDatastoreType store, InstanceIdentifier path, DataObject data); + + @Override + void delete(LogicalDatastoreType store, InstanceIdentifier path); +} diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/BindingAwareBroker.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/BindingAwareBroker.java index db0d674e63..5b700703bc 100644 --- a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/BindingAwareBroker.java +++ b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/sal/binding/api/BindingAwareBroker.java @@ -11,7 +11,7 @@ import org.opendaylight.controller.md.sal.common.api.routing.RoutedRegistration; import org.opendaylight.controller.sal.binding.api.BindingAwareProvider.ProviderFunctionality; import org.opendaylight.controller.sal.binding.api.data.DataBrokerService; import org.opendaylight.controller.sal.binding.api.data.DataProviderService; -import org.opendaylight.yangtools.concepts.Registration; +import org.opendaylight.yangtools.concepts.ObjectRegistration; import org.opendaylight.yangtools.yang.binding.BaseIdentity; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.binding.RpcService; @@ -19,20 +19,20 @@ import org.osgi.framework.BundleContext; /** * Binding-aware core of the SAL layer responsible for wiring the SAL consumers. - * + * * The responsibility of the broker is to maintain registration of SAL * functionality {@link Consumer}s and {@link Provider}s, store provider and * consumer specific context and functionality registration via * {@link ConsumerContext} and provide access to infrastructure services, which * removes direct dependencies between providers and consumers. - * + * * The Binding-aware broker is also responsible for translation from Java * classes modeling the functionality and data to binding-independent form which * is used in SAL Core. - * - * + * + * *

Infrastructure services

Some examples of infrastructure services: - * + * *
    *
  • YANG Module service - see {@link ConsumerContext#getRpcService(Class)}, * {@link ProviderContext} @@ -42,34 +42,34 @@ import org.osgi.framework.BundleContext; *
  • Data Store access and modification - see {@link DataBrokerService} and * {@link DataProviderService} *
- * + * * The services are exposed via session. - * + * *

Session-based access

- * + * * The providers and consumers needs to register in order to use the * binding-independent SAL layer and to expose functionality via SAL layer. - * + * * For more information about session-based access see {@link ConsumerContext} * and {@link ProviderContext} - * - * - * + * + * + * */ public interface BindingAwareBroker { /** * Registers the {@link BindingAwareConsumer}, which will use the SAL layer. - * + * *

* Note that consumer could register additional functionality at later point * by using service and functionality specific APIs. - * + * *

* The consumer is required to use returned session for all communication * with broker or one of the broker services. The session is announced to * the consumer by invoking * {@link Consumer#onSessionInitiated(ConsumerContext)}. - * + * * @param cons * Consumer to be registered. * @return a session specific to consumer registration @@ -82,24 +82,24 @@ public interface BindingAwareBroker { /** * Registers the {@link BindingAwareProvider}, which will use the SAL layer. - * + * *

* During the registration, the broker obtains the initial functionality * from consumer, using the * {@link BindingAwareProvider#getImplementations()}, and register that * functionality into system and concrete infrastructure services. - * + * *

* Note that provider could register additional functionality at later point * by using service and functionality specific APIs. - * + * *

* The consumer is required to use returned session for all * communication with broker or one of the broker services. The session is * announced to the consumer by invoking * {@link BindingAwareProvider#onSessionInitiated(ProviderContext)}. - * - * + * + * * @param prov * Provider to be registered. * @return a session unique to the provider registration. @@ -112,26 +112,26 @@ public interface BindingAwareBroker { /** * {@link BindingAwareConsumer} specific access to the SAL functionality. - * + * *

* ConsumerSession is {@link BindingAwareConsumer}-specific access to the * SAL functionality and infrastructure services. - * + * *

* The session serves to store SAL context (e.g. registration of * functionality) for the consumer and provides access to the SAL * infrastructure services and other functionality provided by * {@link Provider}s. - * - * - * + * + * + * */ public interface ConsumerContext extends RpcConsumerRegistry { /** * Returns a session specific instance (implementation) of requested * binding-aware infrastructural service - * + * * @param service * Broker service * @return Session specific implementation of service @@ -143,19 +143,19 @@ public interface BindingAwareBroker { /** * {@link BindingAwareProvider} specific access to the SAL functionality. - * + * *

* ProviderSession is {@link BindingAwareProvider}-specific access to the * SAL functionality and infrastructure services, which also allows for * exposing the provider's functionality to the other * {@link BindingAwareConsumer}s. - * + * *

* The session serves to store SAL context (e.g. registration of * functionality) for the providers and exposes access to the SAL * infrastructure services, dynamic functionality registration and any other * functionality provided by other {@link BindingAwareConsumer}s. - * + * */ public interface ProviderContext extends ConsumerContext, RpcProviderRegistry { @@ -166,7 +166,7 @@ public interface BindingAwareBroker { void unregisterFunctionality(ProviderFunctionality functionality); } - public interface RpcRegistration extends Registration { + public interface RpcRegistration extends ObjectRegistration { Class getServiceType(); } @@ -177,9 +177,9 @@ public interface BindingAwareBroker { /** * Register particular instance identifier to be processed by this * RpcService - * - * Deprecated in favor of {@link RoutedRegistration#registerPath(Object, Object)}. - * + * + * Deprecated in favor of {@link RoutedRegistration#registerPath(Object, Object)}. + * * @param context * @param instance */ @@ -189,9 +189,9 @@ public interface BindingAwareBroker { /** * Unregister particular instance identifier to be processed by this * RpcService - * - * Deprecated in favor of {@link RoutedRegistration#unregisterPath(Object, Object)}. - * + * + * Deprecated in favor of {@link RoutedRegistration#unregisterPath(Object, Object)}. + * * @param context * @param instance */ diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/RpcProviderRegistryImpl.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/RpcProviderRegistryImpl.java index e0c7d260b5..542dfa7e7b 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/RpcProviderRegistryImpl.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/RpcProviderRegistryImpl.java @@ -247,7 +247,8 @@ public class RpcProviderRegistryImpl implements // public RpcProxyRegistration(Class type, T service, RpcProviderRegistryImpl registry) { super(service); - serviceType = type; + this.serviceType = type; + this.registry = registry; } @Override diff --git a/opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java b/opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java index 0a71ef5315..ba639ad7c2 100644 --- a/opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java +++ b/opendaylight/md-sal/sal-binding-it/src/main/java/org/opendaylight/controller/test/sal/binding/it/TestHelper.java @@ -45,6 +45,13 @@ public class TestHelper { bindingAwareSalBundles(), mavenBundle("commons-codec", "commons-codec").versionAsInProject(), + systemProperty("org.osgi.framework.system.packages.extra").value("sun.nio.ch"), + mavenBundle("io.netty", "netty-common").versionAsInProject(), // + mavenBundle("io.netty", "netty-buffer").versionAsInProject(), // + mavenBundle("io.netty", "netty-handler").versionAsInProject(), // + mavenBundle("io.netty", "netty-codec").versionAsInProject(), // + mavenBundle("io.netty", "netty-transport").versionAsInProject(), // + mavenBundle(CONTROLLER, "protocol-framework").versionAsInProject(), // mavenBundle(CONTROLLER, "config-manager").versionAsInProject(), // // mavenBundle("commons-io", "commons-io").versionAsInProject(), // @@ -64,12 +71,6 @@ public class TestHelper { mavenBundle(CONTROLLER, "config-persister-impl").versionAsInProject(), // - mavenBundle("io.netty", "netty-handler").versionAsInProject(), // - mavenBundle("io.netty", "netty-codec").versionAsInProject(), // - mavenBundle("io.netty", "netty-buffer").versionAsInProject(), // - mavenBundle("io.netty", "netty-transport").versionAsInProject(), // - mavenBundle("io.netty", "netty-common").versionAsInProject(), // - mavenBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.xerces", "2.11.0_1"), mavenBundle("org.eclipse.birt.runtime.3_7_1", "org.apache.xml.resolver", "1.2.0"), diff --git a/opendaylight/md-sal/sal-binding-it/src/test/java/org/opendaylight/controller/test/sal/binding/it/AbstractTest.java b/opendaylight/md-sal/sal-binding-it/src/test/java/org/opendaylight/controller/test/sal/binding/it/AbstractTest.java index 9ac94e7b89..019fc0eb73 100644 --- a/opendaylight/md-sal/sal-binding-it/src/test/java/org/opendaylight/controller/test/sal/binding/it/AbstractTest.java +++ b/opendaylight/md-sal/sal-binding-it/src/test/java/org/opendaylight/controller/test/sal/binding/it/AbstractTest.java @@ -25,6 +25,7 @@ import static org.opendaylight.controller.test.sal.binding.it.TestHelper.junitAn import static org.opendaylight.controller.test.sal.binding.it.TestHelper.mdSalCoreBundles; import static org.ops4j.pax.exam.CoreOptions.mavenBundle; import static org.ops4j.pax.exam.CoreOptions.options; +import static org.ops4j.pax.exam.CoreOptions.systemPackages; import static org.ops4j.pax.exam.CoreOptions.systemProperty; @RunWith(PaxExam.class) @@ -70,6 +71,7 @@ public abstract class AbstractTest { mavenBundle("ch.qos.logback", "logback-core").versionAsInProject(), // mavenBundle("ch.qos.logback", "logback-classic").versionAsInProject(), // systemProperty("osgi.bundles.defaultStartLevel").value("4"), + systemPackages("sun.nio.ch"), mdSalCoreBundles(), diff --git a/opendaylight/md-sal/sal-common-api/pom.xml b/opendaylight/md-sal/sal-common-api/pom.xml index 126fe8d39e..8798897a1d 100644 --- a/opendaylight/md-sal/sal-common-api/pom.xml +++ b/opendaylight/md-sal/sal-common-api/pom.xml @@ -1,17 +1,21 @@ - + 4.0.0 org.opendaylight.controller sal-parent 1.1-SNAPSHOT + sal-common-api + bundle + - scm:git:ssh://git.opendaylight.org:29418/controller.git - scm:git:ssh://git.opendaylight.org:29418/controller.git - https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL - HEAD - + scm:git:ssh://git.opendaylight.org:29418/controller.git + scm:git:ssh://git.opendaylight.org:29418/controller.git + https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL + HEAD + @@ -22,6 +26,9 @@ org.opendaylight.yangtools concepts + + com.google.guava + guava + - bundle diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataBroker.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataBroker.java new file mode 100644 index 0000000000..87bbfd3d06 --- /dev/null +++ b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataBroker.java @@ -0,0 +1,68 @@ +/* + * 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.md.sal.common.api.data; + +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.concepts.Path; + +public interface AsyncDataBroker

, D, L extends AsyncDataChangeListener> extends // + AsyncDataTransactionFactory { + + /** + * + * Scope of Data Change + * + * Represents scope of data change (addition, replacement, deletion). + * + * The terminology for types is reused from LDAP + * + * @see http://www.idevelopment.info/data/LDAP/LDAP_Resources/SEARCH_Setting_the_SCOPE_Parameter.shtml + */ + public enum DataChangeScope { + + /** + * Represents only a direct change of the node, such as replacement of node, + * addition or deletion. + * + */ + BASE, + /** + * Represent a change (addition,replacement,deletion) + * of the node or one of it's direct childs. + * + */ + ONE, + /** + * Represents a change of the node or any of it's child nodes. + * + */ + SUBTREE + } + + @Override + public AsyncReadTransaction newReadOnlyTransaction(); + + @Override + public AsyncReadWriteTransaction newReadWriteTransaction(); + + @Override + public AsyncWriteTransaction newWriteOnlyTransaction(); + + /** + * Registers {@link DataChangeListener} for Data Change callbacks + * which will be triggered on which will be triggered on the store + * + * @param store Logical store in which listener is registered. + * @param path Path (subtree identifier) on which client listener will be invoked. + * @param listener Instance of listener which should be invoked on + * @param triggeringScope Scope of change which triggers callback. + * @return Listener registration of the listener, call {@link ListenerRegistration#close()} + * to stop delivery of change events. + */ + ListenerRegistration registerDataChangeListener(LogicalDatastoreType store, P path, L listener, DataChangeScope triggeringScope); +} diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataChangeEvent.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataChangeEvent.java new file mode 100644 index 0000000000..f612e51747 --- /dev/null +++ b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataChangeEvent.java @@ -0,0 +1,75 @@ +/* + * 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.md.sal.common.api.data; + +import java.util.Map; +import java.util.Set; + +import org.opendaylight.yangtools.concepts.Immutable; +import org.opendaylight.yangtools.concepts.Path; + +public interface AsyncDataChangeEvent

,D> extends Immutable { + /** + * Returns a immutable map of paths and newly created objects + * + * @return map of paths and newly created objects + */ + Map getCreatedData(); + + /** + * Returns a immutable map of paths and respective updated objects after update. + * + * Original state of the object is in + * {@link #getOriginalData()} + * + * @return map of paths and newly created objects + */ + Map getUpdatedData(); + + /** + * Returns a immutable set of removed paths. + * + * Original state of the object is in + * {@link #getOriginalData()} + * + * @return set of removed paths + */ + Set

getRemovedPaths(); + + /** + * Return a immutable map of paths and original state of updated and removed objects. + * + * This map is populated if at changed path was previous object, and captures + * state of previous object. + * + * @return map of paths and original state of updated and removed objects. + */ + Map getOriginalData(); + + /** + * Returns a immutable stable view of data state, which + * captures state of data store before the reported change. + * + * + * The view is rooted at the point where the listener, to which the event is being delivered, was registered. + * + * @return Stable view of data before the change happened, rooted at the listener registration path. + * + */ + D getOriginalSubtree(); + + /** + * Returns a immutable stable view of data, which captures state of data store + * after the reported change. + * + * The view is rooted at the point where the listener, to which the event is being delivered, was registered. + * + * @return Stable view of data after the change happened, rooted at the listener registration path. + */ + D getUpdatedSubtree(); +} diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataChangeListener.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataChangeListener.java new file mode 100644 index 0000000000..49f07bc52b --- /dev/null +++ b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataChangeListener.java @@ -0,0 +1,23 @@ +/* + * 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.md.sal.common.api.data; + +import java.util.EventListener; + +import org.opendaylight.yangtools.concepts.Path; + +public interface AsyncDataChangeListener

, D> extends EventListener { + /** + * Note that this method may be invoked from a shared thread pool, so + * implementations SHOULD NOT perform CPU-intensive operations and they + * definitely MUST NOT invoke any potentially blocking operations. + * + * @param change Data Change Event being delivered. + */ + void onDataChanged(AsyncDataChangeEvent change); +} diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataTransactionFactory.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataTransactionFactory.java new file mode 100644 index 0000000000..732fed0f3f --- /dev/null +++ b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncDataTransactionFactory.java @@ -0,0 +1,20 @@ +/* + * 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.md.sal.common.api.data; + +import org.opendaylight.yangtools.concepts.Path; + +public interface AsyncDataTransactionFactory

, D> { + + AsyncReadTransaction newReadOnlyTransaction(); + + AsyncReadWriteTransaction newReadWriteTransaction(); + + AsyncWriteTransaction newWriteOnlyTransaction(); + +} diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncReadTransaction.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncReadTransaction.java new file mode 100644 index 0000000000..1d1d9101ec --- /dev/null +++ b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncReadTransaction.java @@ -0,0 +1,39 @@ +/* + * 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.md.sal.common.api.data; + +import java.util.concurrent.Future; + +import org.opendaylight.yangtools.concepts.Path; + +import com.google.common.base.Optional; +import com.google.common.util.concurrent.ListenableFuture; + +public interface AsyncReadTransaction

, D> extends AsyncTransaction { + + /** + * + * Reads data from provided logical data store located at provided path + * + * + * @param store + * Logical data store from which read should occur. + * @param path + * Path which uniquely identifies subtree which client want to + * read + * @return Listenable Future which contains read result + *

    + *
  • If data at supplied path exists the {@link Future#get()} + * returns Optional object containing data + *
  • If data at supplied path does not exists the + * {@link Future#get()} returns {@link Optional#absent()}. + *
+ */ + ListenableFuture> read(LogicalDatastoreType store, P path); + +} diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncReadWriteTransaction.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncReadWriteTransaction.java new file mode 100644 index 0000000000..ce740bf41d --- /dev/null +++ b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncReadWriteTransaction.java @@ -0,0 +1,23 @@ +/* + * 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.md.sal.common.api.data; + +import org.opendaylight.yangtools.concepts.Path; + +/** + * Transaction enabling client to have combined transaction, + * which provides read and write capabilities. + * + * + * @param

Path Type + * @param Data Type + */ +public interface AsyncReadWriteTransaction

, D> extends AsyncReadTransaction, + AsyncWriteTransaction { + +} diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncTransaction.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncTransaction.java new file mode 100644 index 0000000000..23ca275ef2 --- /dev/null +++ b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncTransaction.java @@ -0,0 +1,33 @@ +/* + * 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.md.sal.common.api.data; + +import org.opendaylight.yangtools.concepts.Identifiable; +import org.opendaylight.yangtools.concepts.Path; + + +/** + * + * @author + * + * @param

Type of path (subtree identifier), which represents location in tree + * @param Type of data (payload), which represents data payload + */ +public interface AsyncTransaction

,D> extends // + Identifiable, + AutoCloseable { + + @Override + public Object getIdentifier(); + + /** + * Closes transaction and releases all resources associated with it. + */ + @Override + public void close(); +} diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncWriteTransaction.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncWriteTransaction.java new file mode 100644 index 0000000000..35b9914a12 --- /dev/null +++ b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/AsyncWriteTransaction.java @@ -0,0 +1,119 @@ +/* + * 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.md.sal.common.api.data; + +import java.util.concurrent.Future; + +import org.opendaylight.controller.md.sal.common.api.TransactionStatus; +import org.opendaylight.yangtools.concepts.Path; +import org.opendaylight.yangtools.yang.common.RpcResult; + +public interface AsyncWriteTransaction

, D> extends AsyncTransaction { + /** + * Cancels transaction. + * + * Transaction could be only cancelled if it's status + * is {@link TransactionStatus#NEW} or {@link TransactionStatus#SUBMITED} + * + * Invoking cancel() on {@link TransactionStatus#FAILED} or {@link TransactionStatus#CANCELED} + * will have no effect. + * + * @throws IllegalStateException If transaction status is {@link TransactionStatus#COMMITED} + * + */ + public void cancel(); + + /** + * Store a piece of data at specified path. This acts as a add / replace operation, + * which is to say that whole subtree will be replaced by specified path. + * + * If you need add or merge of current object with specified use {@link #merge(LogicalDatastoreType, Path, Object)} + * + * @param store Logical data store which should be modified + * @param path Data object path + * @param data Data object to be written to specified path + * @throws IllegalStateException if the transaction is no longer {@link TransactionStatus#NEW} + */ + public void put(LogicalDatastoreType store, P path, D data); + + /** + * Store a piece of data at specified path. This acts as a merge operation, + * which is to say that any pre-existing data which is not explicitly + * overwritten will be preserved. This means that if you store a container, + * its child lists will be merged. Performing the following put operations: + * + * 1) container { list [ a ] } + * 2) container { list [ b ] } + * + * will result in the following data being present: + * + * container { list [ a, b ] } + * + * This also means that storing the container will preserve any augmentations + * which have been attached to it. + * + * If you require an explicit replace operation, use {@link #put(LogicalDatastoreType, Path, Object)} instead. + * + * @param store Logical data store which should be modified + * @param path Data object path + * @param data Data object to be written to specified path + * @throws IllegalStateException if the transaction is no longer {@link TransactionStatus#NEW} + */ + public void merge(LogicalDatastoreType store, P path, D data); + + /** + * Remove a piece of data from specified path. This operation does not fail + * if the specified path does not exist. + * + * @param store Logical data store which should be modified + * @param path Data object path + * @throws IllegalStateException if the transaction is no longer {@link TransactionStatus#NEW} + */ + public void delete(LogicalDatastoreType store, P path); + + /** + * + * Closes transaction and resources allocated to the transaction. + * + * This call does not change Transaction status. Client SHOULD + * explicitly {@link #commit()} or {@link #cancel()} transaction. + * + * @throws IllegalStateException if the transaction has not been + * updated by invoking {@link #commit()} or {@link #cancel()}. + */ + @Override + public void close(); + + /** + * Initiates a commit of modification. This call logically seals the + * transaction, preventing any the client from interacting with the + * data stores. The transaction is marked as {@link TransactionStatus#SUBMITED} + * and enqueued into the data store backed for processing. + * + *

+ * The successful commit changes the state of the system and may affect + * several components. + * + *

+ * The effects of successful commit of data are described in the + * specifications and YANG models describing the Provider components of + * controller. It is assumed that Consumer has an understanding of this + * changes. + * + * @see DataCommitHandler for further information how two-phase commit is + * processed. + * @param store Identifier of the store, where commit should occur. + * @return Result of the Commit, containing success information or list of + * encountered errors, if commit was not successful. The Future + * blocks until {@link TransactionStatus#COMMITED} or + * {@link TransactionStatus#FAILED} is reached. + * @throws IllegalStateException if the transaction is not {@link TransactionStatus#NEW} + */ + public Future> commit(); + +} diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataChangeListener.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataChangeListener.java index 8787a3fe8d..669baa8d9e 100644 --- a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataChangeListener.java +++ b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/DataChangeListener.java @@ -12,6 +12,12 @@ import java.util.EventListener; import org.opendaylight.yangtools.concepts.Path; public interface DataChangeListener

, D> extends EventListener { - + /** + * Note that this method may be invoked from a shared thread pool, so + * implementations SHOULD NOT perform CPU-intensive operations and they + * definitely MUST NOT invoke any potentially blocking operations. + * + * @param change Data Change Event being delivered. + **/ void onDataChanged(DataChangeEvent change); } diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/LogicalDatastoreType.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/LogicalDatastoreType.java new file mode 100644 index 0000000000..d2e41f1688 --- /dev/null +++ b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/LogicalDatastoreType.java @@ -0,0 +1,31 @@ +/* + * 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.md.sal.common.api.data; + +public enum LogicalDatastoreType { + + /** + * Logical atastore representing operational state of the system + * and it's components + * + * This datastore is used to describe operational state of + * the system and it's operation related data. + * + */ + OPERATIONAL, + /** + * Logical Datastore representing configuration state of the system + * and it's components. + * + * This datastore is used to describe intended state of + * the system and intended operation mode. + * + */ + CONFIGURATION + +} diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionChain.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionChain.java index d542935dd6..e7e0eb0ff8 100644 --- a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionChain.java +++ b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionChain.java @@ -13,17 +13,34 @@ import org.opendaylight.yangtools.concepts.Path; * A chain of transactions. Transactions in a chain need to be committed in sequence and each * transaction should see the effects of previous transactions as if they happened. A chain * makes no guarantees of atomicity, in fact transactions are committed as soon as possible. + * */ -public interface TransactionChain

, D> extends AutoCloseable { +public interface TransactionChain

, D> extends AutoCloseable, AsyncDataTransactionFactory { + /** - * Create a new transaction which will continue the chain. The previous transaction - * has to be either COMMITTED or CANCELLED. + * Create a new read only transaction which will continue the chain. + * The previous read-write transaction has to be either COMMITED or CANCELLED. * * @return New transaction in the chain. - * @throws IllegalStateException if the previous transaction was not COMMITTED or CANCELLED. + * @throws IllegalStateException if the previous transaction was not COMMITED + * or CANCELLED. * @throws TransactionChainClosedException if the chain has been closed. */ - DataModification newTransaction(); + @Override + public AsyncReadTransaction newReadOnlyTransaction(); + + + /** + * Create a new read write transaction which will continue the chain. + * The previous read-write transaction has to be either COMMITED or CANCELLED. + * + * @return New transaction in the chain. + * @throws IllegalStateException if the previous transaction was not COMMITTED + * or CANCELLED. + * @throws TransactionChainClosedException if the chain has been closed. + */ + @Override + public AsyncReadWriteTransaction newReadWriteTransaction(); @Override void close(); diff --git a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionChainListener.java b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionChainListener.java index 4dac6f557e..52b0812736 100644 --- a/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionChainListener.java +++ b/opendaylight/md-sal/sal-common-api/src/main/java/org/opendaylight/controller/md/sal/common/api/data/TransactionChainListener.java @@ -21,7 +21,7 @@ public interface TransactionChainListener extends EventListener { * @param transaction Transaction which caused the chain to fail * @param cause The cause of transaction failure */ - void onTransactionChainFailed(TransactionChain chain, DataModification transaction, Throwable cause); + void onTransactionChainFailed(TransactionChain chain, AsyncTransaction transaction, Throwable cause); /** * Invoked when a transaction chain is completed. A transaction chain is considered completed when it has been diff --git a/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/AbstractRegistration.java b/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/AbstractRegistration.java deleted file mode 100644 index bb8594f35d..0000000000 --- a/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/AbstractRegistration.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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.md.sal.common.impl; - -import org.opendaylight.yangtools.concepts.Registration; - -public abstract class AbstractRegistration implements Registration { - - - private final T instance; - - public AbstractRegistration(T instance) { - super(); - this.instance = instance; - } - - @Override - public final T getInstance() { - return instance; - } - -} diff --git a/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/AbstractRoutedRegistration.java b/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/AbstractRoutedRegistration.java index 4ffb87d5d3..22c458a507 100644 --- a/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/AbstractRoutedRegistration.java +++ b/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/AbstractRoutedRegistration.java @@ -8,9 +8,10 @@ package org.opendaylight.controller.md.sal.common.impl; import org.opendaylight.controller.md.sal.common.api.routing.RoutedRegistration; +import org.opendaylight.yangtools.concepts.AbstractObjectRegistration; import org.opendaylight.yangtools.concepts.Path; -public abstract class AbstractRoutedRegistration, S> extends AbstractRegistration implements +public abstract class AbstractRoutedRegistration, S> extends AbstractObjectRegistration implements RoutedRegistration { public AbstractRoutedRegistration(S instance) { diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataBroker.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataBroker.java new file mode 100644 index 0000000000..5328b79b1f --- /dev/null +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataBroker.java @@ -0,0 +1,23 @@ +/* + * 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.md.sal.dom.api; + +import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker; +import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; + +public interface DOMDataBroker extends AsyncDataBroker, DOMDataChangeListener>{ + @Override + DOMDataReadTransaction newReadOnlyTransaction(); + + @Override + DOMDataReadWriteTransaction newReadWriteTransaction(); + + @Override + DOMDataWriteTransaction newWriteOnlyTransaction(); +} diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataChangeListener.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataChangeListener.java new file mode 100644 index 0000000000..d1f01760d2 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataChangeListener.java @@ -0,0 +1,16 @@ +/* + * 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.md.sal.dom.api; + +import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener; +import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; + +public interface DOMDataChangeListener extends AsyncDataChangeListener> { + +} diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataReadTransaction.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataReadTransaction.java new file mode 100644 index 0000000000..5baa5e72d3 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataReadTransaction.java @@ -0,0 +1,16 @@ +/* + * 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.md.sal.dom.api; + +import org.opendaylight.controller.md.sal.common.api.data.AsyncReadTransaction; +import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; + +public interface DOMDataReadTransaction extends AsyncReadTransaction> { + +} diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataReadWriteTransaction.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataReadWriteTransaction.java new file mode 100644 index 0000000000..55600b0730 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataReadWriteTransaction.java @@ -0,0 +1,16 @@ +/* + * 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.md.sal.dom.api; + +import org.opendaylight.controller.md.sal.common.api.data.AsyncReadWriteTransaction; +import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; + +public interface DOMDataReadWriteTransaction extends DOMDataReadTransaction, DOMDataWriteTransaction, AsyncReadWriteTransaction> { + +} diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataWriteTransaction.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataWriteTransaction.java new file mode 100644 index 0000000000..9415973de5 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/md/sal/dom/api/DOMDataWriteTransaction.java @@ -0,0 +1,16 @@ +/* + * 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.md.sal.dom.api; + +import org.opendaylight.controller.md.sal.common.api.data.AsyncWriteTransaction; +import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; + +public interface DOMDataWriteTransaction extends AsyncWriteTransaction> { + +} diff --git a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/Broker.java b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/Broker.java index 6af06255c7..72df8cb553 100644 --- a/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/Broker.java +++ b/opendaylight/md-sal/sal-dom-api/src/main/java/org/opendaylight/controller/sal/core/api/Broker.java @@ -16,7 +16,7 @@ import org.opendaylight.controller.sal.core.api.data.DataProviderService; import org.opendaylight.controller.sal.core.api.notify.NotificationPublishService; import org.opendaylight.controller.sal.core.api.notify.NotificationService; import org.opendaylight.yangtools.concepts.ListenerRegistration; -import org.opendaylight.yangtools.concepts.Registration; +import org.opendaylight.yangtools.concepts.ObjectRegistration; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.data.api.CompositeNode; @@ -237,7 +237,7 @@ public interface Broker { ListenerRegistration addRpcRegistrationListener(RpcRegistrationListener listener); } - public interface RpcRegistration extends Registration { + public interface RpcRegistration extends ObjectRegistration { QName getType(); @Override diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/NotificationRouterImpl.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/NotificationRouterImpl.java index 50af3fbfc1..7fba31114f 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/NotificationRouterImpl.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/sal/dom/broker/impl/NotificationRouterImpl.java @@ -11,7 +11,7 @@ import java.util.Collection; import org.opendaylight.controller.sal.core.api.notify.NotificationListener; import org.opendaylight.controller.sal.dom.broker.spi.NotificationRouter; -import org.opendaylight.yangtools.concepts.AbstractObjectRegistration; +import org.opendaylight.yangtools.concepts.AbstractListenerRegistration; import org.opendaylight.yangtools.concepts.Registration; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.CompositeNode; @@ -25,12 +25,12 @@ import com.google.common.collect.Multimaps; public class NotificationRouterImpl implements NotificationRouter { private static Logger log = LoggerFactory.getLogger(NotificationRouterImpl.class); - private final Multimap> listeners = Multimaps.synchronizedSetMultimap(HashMultimap.>create()); + private final Multimap listeners = Multimaps.synchronizedSetMultimap(HashMultimap.create()); // private Registration defaultListener; private void sendNotification(CompositeNode notification) { final QName type = notification.getNodeType(); - final Collection> toNotify = listeners.get(type); + final Collection toNotify = listeners.get(type); log.trace("Publishing notification " + type); if ((toNotify == null) || toNotify.isEmpty()) { @@ -38,7 +38,7 @@ public class NotificationRouterImpl implements NotificationRouter { return; } - for (Registration listener : toNotify) { + for (ListenerRegistration listener : toNotify) { try { // FIXME: ensure that notification is immutable listener.getInstance().onNotification(notification); @@ -60,7 +60,7 @@ public class NotificationRouterImpl implements NotificationRouter { return ret; } - private class ListenerRegistration extends AbstractObjectRegistration { + private class ListenerRegistration extends AbstractListenerRegistration { final QName type; diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStore.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStore.java new file mode 100644 index 0000000000..c82a2b855f --- /dev/null +++ b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStore.java @@ -0,0 +1,60 @@ +/* + * 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.sal.core.spi.data; + +import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope; +import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeListener; +import org.opendaylight.controller.md.sal.common.api.data.DataChangeListener; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; + +public interface DOMStore { + + /** + * + * Creates a read only transaction + * + * @return + */ + DOMStoreReadTransaction newReadOnlyTransaction(); + + /** + * Creates write only transaction + * + * @return + */ + DOMStoreWriteTransaction newWriteOnlyTransaction(); + + /** + * Creates Read-Write transaction + * + * @return + */ + DOMStoreReadWriteTransaction newReadWriteTransaction(); + + /** + * Registers {@link DataChangeListener} for Data Change callbacks + * which will be triggered on the change of provided subpath. What + * constitutes a change depends on the @scope parameter. + * + * Listener upon registration receives an initial callback + * {@link AsyncDataChangeListener#onDataChanged(org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent)} + * which contains stable view of data tree at the time of registration. + * + * @param path Path (subtree identifier) on which client listener will be invoked. + * @param listener Instance of listener which should be invoked on + * @param scope Scope of change which triggers callback. + * @return Listener Registration object, which client may use to close registration + * / interest on receiving data changes. + * + */ + >> ListenerRegistration registerChangeListener( + InstanceIdentifier path, L listener, DataChangeScope scope); + +} diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreReadTransaction.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreReadTransaction.java new file mode 100644 index 0000000000..733c10926c --- /dev/null +++ b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreReadTransaction.java @@ -0,0 +1,37 @@ +/* + * 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.sal.core.spi.data; + +import java.util.concurrent.Future; + +import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; + +import com.google.common.base.Optional; +import com.google.common.util.concurrent.ListenableFuture; + +public interface DOMStoreReadTransaction extends DOMStoreTransaction { + + /** + * + * Reads data from provided logical data store located at provided path + * + * + * @param path + * Path which uniquely identifies subtree which client want to + * read + * @return Listenable Future which contains read result + *

    + *
  • If data at supplied path exists the {@link Future#get()} + * returns Optional object containing data + *
  • If data at supplied path does not exists the + * {@link Future#get()} returns {@link Optional#absent()}. + *
+ */ + ListenableFuture>> read(InstanceIdentifier path); +} diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreReadWriteTransaction.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreReadWriteTransaction.java new file mode 100644 index 0000000000..72774062f4 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreReadWriteTransaction.java @@ -0,0 +1,15 @@ +/* + * 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.sal.core.spi.data; + +/** + * Combination of a {@link DOMStoreReadTransaction} and {@link DOMStoreWriteTransaction}. + */ +public interface DOMStoreReadWriteTransaction extends DOMStoreReadTransaction, DOMStoreWriteTransaction { + +} diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreThreePhaseCommitCohort.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreThreePhaseCommitCohort.java new file mode 100644 index 0000000000..986a153efb --- /dev/null +++ b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreThreePhaseCommitCohort.java @@ -0,0 +1,62 @@ +/* + * 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.sal.core.spi.data; + +import com.google.common.util.concurrent.ListenableFuture; + +/** + * Interface implemented by the {@link DOMStore} and exposed for each {@link DOMStoreWriteTransaction} + * upon its transition to Ready state. The frontend (DOMStore user) uses this interface to drive the + * commit procedure across potentially multiple DOMStores using the Three-Phase-Commit (3PC) Protocol, + * as described in {@link https://en.wikipedia.org/wiki/Three-phase_commit}. + */ +public interface DOMStoreThreePhaseCommitCohort { + + /** + * Sends transaction associated with this three phase commit instance to the + * participant, participant votes on the transaction, if the transaction + * should be committed or aborted. + * + * @return ListenableFuture with vote of the participant. Vote + * {@link ListenableFuture#get()} is following: + *
    + *
  • + * true if transaction is approved by data store. + *
  • false if the transaction is not approved by data store and + * should be aborted. + */ + ListenableFuture canCommit(); + + /** + * Initiates a pre-commit phase of associated transaction on datastore. + * + * This message is valid only and only if and only if the participant responded + * on {@link #canCommit()} call with positive response. + * + * @return ListenableFuture representing acknowledgment for participant + * that pre-commit message was received and processed. + */ + ListenableFuture preCommit(); + + /** + * Initiates a abort phase of associated transaction on data store. + * + * @return ListenableFuture representing acknowledgment for participant + * that abort message was received. + */ + ListenableFuture abort(); + + /** + * Initiates a commit phase on of associated transaction on data store. + * + * @return ListenableFuture representing acknowledgment for participant + * that commit message was received and commit of transaction was + * processed. + */ + ListenableFuture commit(); +} diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreTransaction.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreTransaction.java new file mode 100644 index 0000000000..76ea78b299 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreTransaction.java @@ -0,0 +1,28 @@ +/* + * 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.sal.core.spi.data; + +import org.opendaylight.yangtools.concepts.Identifiable; + +/** + * DOM Data Store transaction + * + * See {@link DOMStoreReadTransaction}, {@link DOMStoreWriteTransaction} and {@link DOMStoreReadWriteTransaction} + * for specific transaction types. + * + */ +public interface DOMStoreTransaction extends AutoCloseable, Identifiable { + /** + * Unique identifier of the transaction + */ + @Override + public Object getIdentifier(); + + @Override + void close(); +} diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreWriteTransaction.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreWriteTransaction.java new file mode 100644 index 0000000000..6761bc1968 --- /dev/null +++ b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/DOMStoreWriteTransaction.java @@ -0,0 +1,57 @@ +/* + * 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.sal.core.spi.data; + +import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; +import org.opendaylight.yangtools.concepts.Path; +import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; + +public interface DOMStoreWriteTransaction extends DOMStoreTransaction { + + /** + * Store a provided data at specified path. This acts as a add / replace + * operation, which is to say that whole subtree will be replaced by + * specified path. + * + * If you need add or merge of current object with specified use + * {@link #merge(LogicalDatastoreType, Path, Object)} + * + * + * @param path + * @param data + * Data object to be written + * + * @throws IllegalStateException + * if the client code already sealed transaction and invoked + * {@link #ready()} + */ + void write(InstanceIdentifier path, NormalizedNode data); + + /** + * + * Deletes data and whole subtree located at provided path. + * + * @param path + * Path to delete + * @throws IllegalStateException + * if the client code already sealed transaction and invoked + * {@link #ready()} + */ + void delete(InstanceIdentifier path); + + /** + * + * Seals transaction, and returns three-phase commit cohort associated + * with this transaction and DOM Store to be coordinated by coordinator. + * + * @return Three Phase Commit Cohort instance for this transaction. + */ + DOMStoreThreePhaseCommitCohort ready(); + +} diff --git a/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/package-info.java b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/package-info.java new file mode 100644 index 0000000000..ec3b69813e --- /dev/null +++ b/opendaylight/md-sal/sal-dom-spi/src/main/java/org/opendaylight/controller/sal/core/spi/data/package-info.java @@ -0,0 +1,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 + */ +package org.opendaylight.controller.sal.core.spi.data; \ No newline at end of file diff --git a/opendaylight/md-sal/sal-netconf-connector/pom.xml b/opendaylight/md-sal/sal-netconf-connector/pom.xml index 777709b06a..182441d3f5 100644 --- a/opendaylight/md-sal/sal-netconf-connector/pom.xml +++ b/opendaylight/md-sal/sal-netconf-connector/pom.xml @@ -41,6 +41,10 @@ org.opendaylight.yangtools yang-data-impl + + org.opendaylight.yangtools + yang-parser-impl + org.opendaylight.controller sal-broker-impl diff --git a/opendaylight/md-sal/samples/toaster-it/src/test/java/org/opendaylight/controller/sample/toaster/it/ToasterTest.java b/opendaylight/md-sal/samples/toaster-it/src/test/java/org/opendaylight/controller/sample/toaster/it/ToasterTest.java index 38a4dd4661..000783bd07 100644 --- a/opendaylight/md-sal/samples/toaster-it/src/test/java/org/opendaylight/controller/sample/toaster/it/ToasterTest.java +++ b/opendaylight/md-sal/samples/toaster-it/src/test/java/org/opendaylight/controller/sample/toaster/it/ToasterTest.java @@ -52,6 +52,7 @@ public class ToasterTest { mavenBundle("ch.qos.logback", "logback-core").versionAsInProject(), // mavenBundle("ch.qos.logback", "logback-classic").versionAsInProject(), // systemProperty("osgi.bundles.defaultStartLevel").value("4"), + systemPackages("sun.nio.ch"), toasterBundles(), mdSalCoreBundles(), diff --git a/opendaylight/md-sal/test/sal-rest-connector-it/pom.xml b/opendaylight/md-sal/test/sal-rest-connector-it/pom.xml index 15fee781ef..a9fc739456 100644 --- a/opendaylight/md-sal/test/sal-rest-connector-it/pom.xml +++ b/opendaylight/md-sal/test/sal-rest-connector-it/pom.xml @@ -136,7 +136,7 @@ io.netty netty-all - 4.0.10.Final + ${netty.version} test diff --git a/opendaylight/md-sal/topology-manager/src/main/java/org/opendaylight/md/controller/topology/manager/FlowCapableTopologyProvider.xtend b/opendaylight/md-sal/topology-manager/src/main/java/org/opendaylight/md/controller/topology/manager/FlowCapableTopologyProvider.xtend index 036fe733aa..73e03d1e7d 100644 --- a/opendaylight/md-sal/topology-manager/src/main/java/org/opendaylight/md/controller/topology/manager/FlowCapableTopologyProvider.xtend +++ b/opendaylight/md-sal/topology-manager/src/main/java/org/opendaylight/md/controller/topology/manager/FlowCapableTopologyProvider.xtend @@ -14,6 +14,7 @@ import org.opendaylight.yangtools.yang.binding.NotificationListener import org.slf4j.LoggerFactory import org.opendaylight.controller.sal.binding.api.AbstractBindingAwareProvider import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext +import org.osgi.framework.BundleContext; class FlowCapableTopologyProvider extends AbstractBindingAwareProvider implements AutoCloseable { @@ -35,7 +36,11 @@ class FlowCapableTopologyProvider extends AbstractBindingAwareProvider implement LOG.info("FlowCapableTopologyProvider stopped."); listenerRegistration?.close(); } - + + /** + * Gets called on start of a bundle. + * @param session + */ override onSessionInitiated(ProviderContext session) { dataService = session.getSALService(DataProviderService) notificationService = session.getSALService(NotificationProviderService) @@ -43,6 +48,14 @@ class FlowCapableTopologyProvider extends AbstractBindingAwareProvider implement exporter.start(); listenerRegistration = notificationService.registerNotificationListener(exporter); } + + /** + * Gets called during stop bundle + * @param context The execution context of the bundle being stopped. + */ + override stopImpl(BundleContext context) { + close(); + } } diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/Activator.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/Activator.java index b39549ed5b..421870ca36 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/Activator.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/Activator.java @@ -19,6 +19,9 @@ import org.osgi.util.tracker.ServiceTrackerCustomizer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Dictionary; +import java.util.Hashtable; + import static com.google.common.base.Preconditions.checkState; public class Activator implements BundleActivator { @@ -88,7 +91,9 @@ public class Activator implements BundleActivator { public void run() { NetconfOperationServiceFactoryImpl factory = new NetconfOperationServiceFactoryImpl(yangStoreService); logger.debug("Registering into OSGi"); - osgiRegistration = context.registerService(NetconfOperationServiceFactory.class, factory, null); + Dictionary properties = new Hashtable<>(); + properties.put("name", "config-netconf-connector"); + osgiRegistration = context.registerService(NetconfOperationServiceFactory.class, factory, properties); } } } diff --git a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceFactoryImpl.java b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceFactoryImpl.java index 4ca71ae288..b8b7fcb47f 100644 --- a/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceFactoryImpl.java +++ b/opendaylight/netconf/config-netconf-connector/src/main/java/org/opendaylight/controller/netconf/confignetconfconnector/osgi/NetconfOperationServiceFactoryImpl.java @@ -66,7 +66,7 @@ public class NetconfOperationServiceFactoryImpl implements NetconfOperationServi } @Override - public NetconfOperationServiceImpl createService(long netconfSessionId, String netconfSessionIdForReporting) { + public NetconfOperationServiceImpl createService(String netconfSessionIdForReporting) { try { return new NetconfOperationServiceImpl(yangStoreService, jmxClient, netconfSessionIdForReporting); } catch (YangStoreException e) { diff --git a/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java b/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java index b52328f631..8b6b1aefc1 100644 --- a/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java +++ b/opendaylight/netconf/config-netconf-connector/src/test/java/org/opendaylight/controller/netconf/confignetconfconnector/NetconfMappingTest.java @@ -52,10 +52,10 @@ import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStore import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreSnapshot; import org.opendaylight.controller.netconf.confignetconfconnector.transactions.TransactionProvider; import org.opendaylight.controller.netconf.impl.mapping.operations.DefaultCloseSession; -import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationRouterImpl; import org.opendaylight.controller.netconf.impl.osgi.NetconfOperationServiceSnapshot; import org.opendaylight.controller.netconf.mapping.api.HandlingPriority; import org.opendaylight.controller.netconf.mapping.api.NetconfOperation; +import org.opendaylight.controller.netconf.mapping.api.NetconfOperationChainedExecution; import org.opendaylight.controller.netconf.util.test.XmlFileLoader; import org.opendaylight.controller.netconf.util.xml.XmlElement; import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; @@ -681,7 +681,7 @@ public class NetconfMappingTest extends AbstractConfigTest { Preconditions.checkState(priority != HandlingPriority.CANNOT_HANDLE); - final Document response = op.handle(request, NetconfOperationRouterImpl.EXECUTION_TERMINATION_POINT); + final Document response = op.handle(request, NetconfOperationChainedExecution.EXECUTION_TERMINATION_POINT); logger.debug("Got response\n{}", XmlUtil.toString(response)); return response.getDocumentElement(); } diff --git a/opendaylight/netconf/config-persister-impl/pom.xml b/opendaylight/netconf/config-persister-impl/pom.xml index c0b9f68814..7b4511e1f9 100644 --- a/opendaylight/netconf/config-persister-impl/pom.xml +++ b/opendaylight/netconf/config-persister-impl/pom.xml @@ -20,10 +20,6 @@ ${project.groupId} netconf-api - - ${project.groupId} - netconf-client - ${project.groupId} netconf-util @@ -61,6 +57,7 @@ org.opendaylight.yangtools mockito-configuration + test commons-io @@ -83,32 +80,7 @@ org.opendaylight.controller.netconf.persist.impl.osgi.ConfigPersisterActivator - org.opendaylight.controller.config.persister.storage.adapter - - - com.google.common.base, - com.google.common.collect, - javax.management, - javax.xml.parsers, - org.opendaylight.controller.config.persist.api, - org.opendaylight.controller.netconf.api, - org.opendaylight.controller.netconf.api.jmx, - org.opendaylight.controller.netconf.client, - org.opendaylight.controller.netconf.util.osgi, - org.opendaylight.controller.netconf.util.xml, - org.opendaylight.controller.netconf.util.messages, - io.netty.channel, - io.netty.channel.nio, - io.netty.util.concurrent, - org.osgi.framework, - org.slf4j, - org.w3c.dom, - org.xml.sax, - javax.xml.namespace, - javax.xml.xpath, - org.opendaylight.controller.config.api, - org.opendaylight.controller.netconf.util - + org.opendaylight.controller.config.persister.storage.adapter diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/CapabilityStrippingConfigSnapshotHolder.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/CapabilityStrippingConfigSnapshotHolder.java index d9c5dfaded..ab353e349b 100644 --- a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/CapabilityStrippingConfigSnapshotHolder.java +++ b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/CapabilityStrippingConfigSnapshotHolder.java @@ -5,6 +5,7 @@ * 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.persist.impl; import com.google.common.annotations.VisibleForTesting; @@ -18,54 +19,50 @@ import org.w3c.dom.Element; import java.util.Collections; import java.util.HashSet; -import java.util.Iterator; import java.util.Map.Entry; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; -import java.util.regex.Pattern; - -import static com.google.common.base.Preconditions.checkState; +/** + * Inspects snapshot xml to be stored, remove all capabilities that are not referenced by it. + * Useful when persisting current configuration. + */ public class CapabilityStrippingConfigSnapshotHolder implements ConfigSnapshotHolder { private static final Logger logger = LoggerFactory.getLogger(CapabilityStrippingConfigSnapshotHolder.class); private final String configSnapshot; private final StripCapabilitiesResult stripCapabilitiesResult; - public CapabilityStrippingConfigSnapshotHolder(Element snapshot, Set capabilities, Pattern ignoredMissingCapabilityRegex) { + public CapabilityStrippingConfigSnapshotHolder(Element snapshot, Set capabilities) { final XmlElement configElement = XmlElement.fromDomElement(snapshot); configSnapshot = XmlUtil.toString(configElement.getDomElement()); - stripCapabilitiesResult = stripCapabilities(configElement, capabilities, ignoredMissingCapabilityRegex); + stripCapabilitiesResult = stripCapabilities(configElement, capabilities); } private static class StripCapabilitiesResult { - private final SortedSet requiredCapabilities, missingNamespaces; + private final SortedSet requiredCapabilities, obsoleteCapabilities; - private StripCapabilitiesResult(SortedSet requiredCapabilities, SortedSet missingNamespaces) { + private StripCapabilitiesResult(SortedSet requiredCapabilities, SortedSet obsoleteCapabilities) { this.requiredCapabilities = Collections.unmodifiableSortedSet(requiredCapabilities); - this.missingNamespaces = Collections.unmodifiableSortedSet(missingNamespaces); + this.obsoleteCapabilities = Collections.unmodifiableSortedSet(obsoleteCapabilities); } } @VisibleForTesting - static StripCapabilitiesResult stripCapabilities(XmlElement configElement, Set allCapabilitiesFromHello, - Pattern ignoredMissingCapabilityRegex) { + static StripCapabilitiesResult stripCapabilities(XmlElement configElement, Set allCapabilitiesFromHello) { // collect all namespaces Set foundNamespacesInXML = getNamespaces(configElement); logger.trace("All capabilities {}\nFound namespaces in XML {}", allCapabilitiesFromHello, foundNamespacesInXML); // required are referenced both in xml and hello SortedSet requiredCapabilities = new TreeSet<>(); // can be removed - Set obsoleteCapabilities = new HashSet<>(); - // are in xml but not in hello - SortedSet missingNamespaces = new TreeSet<>(foundNamespacesInXML); + SortedSet obsoleteCapabilities = new TreeSet<>(); for (String capability : allCapabilitiesFromHello) { String namespace = capability.replaceAll("\\?.*",""); if (foundNamespacesInXML.contains(namespace)) { requiredCapabilities.add(capability); - checkState(missingNamespaces.remove(namespace)); } else { obsoleteCapabilities.add(capability); } @@ -74,17 +71,7 @@ public class CapabilityStrippingConfigSnapshotHolder implements ConfigSnapshotHo logger.trace("Required capabilities {}, \nObsolete capabilities {}", requiredCapabilities, obsoleteCapabilities); - for(Iterator iterator = missingNamespaces.iterator();iterator.hasNext(); ){ - String capability = iterator.next(); - if (ignoredMissingCapabilityRegex.matcher(capability).matches()){ - logger.trace("Ignoring missing capability {}", capability); - iterator.remove(); - } - } - if (missingNamespaces.size() > 0) { - logger.warn("Some capabilities are missing: {}", missingNamespaces); - } - return new StripCapabilitiesResult(requiredCapabilities, missingNamespaces); + return new StripCapabilitiesResult(requiredCapabilities, obsoleteCapabilities); } static Set getNamespaces(XmlElement element){ @@ -94,7 +81,6 @@ public class CapabilityStrippingConfigSnapshotHolder implements ConfigSnapshotHo result.add(attribute.getValue().getValue()); } } - //element.getAttributes() for(XmlElement child: element.getChildElements()) { result.addAll(getNamespaces(child)); } @@ -107,8 +93,8 @@ public class CapabilityStrippingConfigSnapshotHolder implements ConfigSnapshotHo } @VisibleForTesting - Set getMissingNamespaces(){ - return stripCapabilitiesResult.missingNamespaces; + Set getObsoleteCapabilities(){ + return stripCapabilitiesResult.obsoleteCapabilities; } @Override diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPersisterNotificationHandler.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPersisterNotificationHandler.java index 2d89bbc55c..eb6fd2722a 100644 --- a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPersisterNotificationHandler.java +++ b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPersisterNotificationHandler.java @@ -23,7 +23,6 @@ import javax.management.NotificationListener; import javax.management.ObjectName; import java.io.Closeable; import java.io.IOException; -import java.util.regex.Pattern; /** * Responsible for listening for notifications from netconf (via JMX) containing latest @@ -39,9 +38,9 @@ public class ConfigPersisterNotificationHandler implements Closeable { public ConfigPersisterNotificationHandler(MBeanServerConnection mBeanServerConnection, - Persister persisterAggregator, Pattern ignoredMissingCapabilityRegex) { + Persister persisterAggregator) { this.mBeanServerConnection = mBeanServerConnection; - listener = new ConfigPersisterNotificationListener(persisterAggregator, ignoredMissingCapabilityRegex); + listener = new ConfigPersisterNotificationListener(persisterAggregator); registerAsJMXListener(mBeanServerConnection, listener); } @@ -73,17 +72,16 @@ class ConfigPersisterNotificationListener implements NotificationListener { private static final Logger logger = LoggerFactory.getLogger(ConfigPersisterNotificationListener.class); private final Persister persisterAggregator; - private final Pattern ignoredMissingCapabilityRegex; - ConfigPersisterNotificationListener(Persister persisterAggregator, Pattern ignoredMissingCapabilityRegex) { + ConfigPersisterNotificationListener(Persister persisterAggregator) { this.persisterAggregator = persisterAggregator; - this.ignoredMissingCapabilityRegex = ignoredMissingCapabilityRegex; } @Override public void handleNotification(Notification notification, Object handback) { - if (notification instanceof NetconfJMXNotification == false) + if (notification instanceof NetconfJMXNotification == false) { return; + } // Socket should not be closed at this point // Activator unregisters this as JMX listener before close is called @@ -98,14 +96,15 @@ class ConfigPersisterNotificationListener implements NotificationListener { logger.warn("Exception occured during notification handling: ", e); throw e; } - } else + } else { throw new IllegalStateException("Unknown config registry notification type " + notification); + } } private void handleAfterCommitNotification(final CommitJMXNotification notification) { try { persisterAggregator.persistConfig(new CapabilityStrippingConfigSnapshotHolder(notification.getConfigSnapshot(), - notification.getCapabilities(), ignoredMissingCapabilityRegex)); + notification.getCapabilities())); logger.trace("Configuration persisted successfully"); } catch (IOException e) { throw new RuntimeException("Unable to persist configuration snapshot", e); diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusher.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusher.java index ea2a46dba5..6dba9ac64e 100644 --- a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusher.java +++ b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusher.java @@ -8,14 +8,21 @@ package org.opendaylight.controller.netconf.persist.impl; +import com.google.common.base.Function; import com.google.common.base.Preconditions; +import com.google.common.base.Stopwatch; +import com.google.common.collect.Collections2; import org.opendaylight.controller.config.api.ConflictingVersionException; import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; import org.opendaylight.controller.netconf.api.NetconfMessage; -import org.opendaylight.controller.netconf.client.NetconfClient; -import org.opendaylight.controller.netconf.client.NetconfClientDispatcher; +import org.opendaylight.controller.netconf.mapping.api.Capability; +import org.opendaylight.controller.netconf.mapping.api.HandlingPriority; +import org.opendaylight.controller.netconf.mapping.api.NetconfOperation; +import org.opendaylight.controller.netconf.mapping.api.NetconfOperationChainedExecution; +import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService; +import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory; import org.opendaylight.controller.netconf.util.NetconfUtil; -import org.opendaylight.controller.netconf.util.messages.NetconfHelloMessageAdditionalHeader; import org.opendaylight.controller.netconf.util.xml.XmlElement; import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; import org.opendaylight.controller.netconf.util.xml.XmlUtil; @@ -28,38 +35,36 @@ import org.xml.sax.SAXException; import javax.annotation.concurrent.Immutable; import java.io.IOException; import java.io.InputStream; -import java.util.Collections; +import java.util.Collection; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; +import java.util.Map.Entry; import java.util.Set; -import java.util.concurrent.ExecutionException; +import java.util.TreeMap; import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; @Immutable public class ConfigPusher { private static final Logger logger = LoggerFactory.getLogger(ConfigPusher.class); - private final ConfigPusherConfiguration configuration; + private final long maxWaitForCapabilitiesMillis; + private final long conflictingVersionTimeoutMillis; + private final NetconfOperationServiceFactory configNetconfConnector; - public ConfigPusher(ConfigPusherConfiguration configuration) { - this.configuration = configuration; + public ConfigPusher(NetconfOperationServiceFactory configNetconfConnector, long maxWaitForCapabilitiesMillis, + long conflictingVersionTimeoutMillis) { + this.configNetconfConnector = configNetconfConnector; + this.maxWaitForCapabilitiesMillis = maxWaitForCapabilitiesMillis; + this.conflictingVersionTimeoutMillis = conflictingVersionTimeoutMillis; } - public synchronized LinkedHashMap pushConfigs( - List configs) throws InterruptedException { + public synchronized LinkedHashMap pushConfigs(List configs) { logger.debug("Last config snapshots to be pushed to netconf: {}", configs); - - // first just make sure we can connect to netconf, even if nothing is being pushed - { - NetconfClient netconfClient = makeNetconfConnection(Collections.emptySet()); - Util.closeClientAndDispatcher(netconfClient); - } - LinkedHashMap result = new LinkedHashMap<>(); + LinkedHashMap result = new LinkedHashMap<>(); // start pushing snapshots: for (ConfigSnapshotHolder configSnapshotHolder : configs) { - EditAndCommitResponseWithRetries editAndCommitResponseWithRetries = pushSnapshotWithRetries(configSnapshotHolder); + EditAndCommitResponse editAndCommitResponseWithRetries = pushConfigWithConflictingVersionRetries(configSnapshotHolder); logger.debug("Config snapshot pushed successfully: {}, result: {}", configSnapshotHolder, result); result.put(configSnapshotHolder, editAndCommitResponseWithRetries); } @@ -68,106 +73,101 @@ public class ConfigPusher { } /** - * Checks for ConflictingVersionException and retries until optimistic lock succeeds or maximal - * number of attempts is reached. + * First calls {@link #getOperationServiceWithRetries(java.util.Set, String)} in order to wait until + * expected capabilities are present, then tries to push configuration. If {@link ConflictingVersionException} + * is caught, whole process is retried - new service instance need to be obtained from the factory. Closes + * {@link NetconfOperationService} after each use. */ - private synchronized EditAndCommitResponseWithRetries pushSnapshotWithRetries(ConfigSnapshotHolder configSnapshotHolder) - throws InterruptedException { - - ConflictingVersionException lastException = null; - int maxAttempts = configuration.netconfPushConfigAttempts; - - for (int retryAttempt = 1; retryAttempt <= maxAttempts; retryAttempt++) { - NetconfClient netconfClient = makeNetconfConnection(configSnapshotHolder.getCapabilities()); - logger.trace("Pushing following xml to netconf {}", configSnapshotHolder); - try { - EditAndCommitResponse editAndCommitResponse = pushLastConfig(configSnapshotHolder, netconfClient); - return new EditAndCommitResponseWithRetries(editAndCommitResponse, retryAttempt); + private synchronized EditAndCommitResponse pushConfigWithConflictingVersionRetries(ConfigSnapshotHolder configSnapshotHolder) { + ConflictingVersionException lastException; + Stopwatch stopwatch = new Stopwatch().start(); + do { + try (NetconfOperationService operationService = getOperationServiceWithRetries(configSnapshotHolder.getCapabilities(), configSnapshotHolder.toString())) { + return pushConfig(configSnapshotHolder, operationService); } catch (ConflictingVersionException e) { - logger.debug("Conflicting version detected, will retry after timeout"); lastException = e; - Thread.sleep(configuration.netconfPushConfigDelayMs); - } catch (RuntimeException e) { - throw new IllegalStateException("Unable to load " + configSnapshotHolder, e); - } finally { - Util.closeClientAndDispatcher(netconfClient); + logger.debug("Conflicting version detected, will retry after timeout"); + sleep(); } - } - throw new IllegalStateException("Maximum attempt count has been reached for pushing " + configSnapshotHolder, + } while (stopwatch.elapsed(TimeUnit.MILLISECONDS) < conflictingVersionTimeoutMillis); + throw new IllegalStateException("Max wait for conflicting version stabilization timeout after " + stopwatch.elapsed(TimeUnit.MILLISECONDS) + " ms", lastException); } - /** - * @param expectedCaps capabilities that server hello must contain. Will retry until all are found or throws RuntimeException. - * If empty set is provided, will only make sure netconf client successfuly connected to the server. - * @return NetconfClient that has all required capabilities from server. - */ - private synchronized NetconfClient makeNetconfConnection(Set expectedCaps) throws InterruptedException { - - // TODO think about moving capability subset check to netconf client - // could be utilized by integration tests - - final long pollingStartNanos = System.nanoTime(); - final long deadlineNanos = pollingStartNanos + TimeUnit.MILLISECONDS.toNanos(configuration.netconfCapabilitiesWaitTimeoutMs); - int attempt = 0; - - NetconfHelloMessageAdditionalHeader additionalHeader = new NetconfHelloMessageAdditionalHeader("unknown", - configuration.netconfAddress.getAddress().getHostAddress(), - Integer.toString(configuration.netconfAddress.getPort()), "tcp", "persister"); - - Set latestCapabilities = null; - while (System.nanoTime() < deadlineNanos) { - attempt++; - NetconfClientDispatcher netconfClientDispatcher = new NetconfClientDispatcher(configuration.eventLoopGroup, - configuration.eventLoopGroup, additionalHeader, configuration.connectionAttemptTimeoutMs); - NetconfClient netconfClient; + private NetconfOperationService getOperationServiceWithRetries(Set expectedCapabilities, String idForReporting) { + Stopwatch stopwatch = new Stopwatch().start(); + NotEnoughCapabilitiesException lastException; + do { try { - netconfClient = new NetconfClient(this.toString(), configuration.netconfAddress, configuration.connectionAttemptDelayMs, netconfClientDispatcher); - } catch (IllegalStateException e) { - logger.debug("Netconf {} was not initialized or is not stable, attempt {}", configuration.netconfAddress, attempt, e); - netconfClientDispatcher.close(); - Thread.sleep(configuration.connectionAttemptDelayMs); - continue; - } - latestCapabilities = netconfClient.getCapabilities(); - if (Util.isSubset(netconfClient, expectedCaps)) { - logger.debug("Hello from netconf stable with {} capabilities", latestCapabilities); - logger.trace("Session id received from netconf server: {}", netconfClient.getClientSession()); - return netconfClient; + return getOperationService(expectedCapabilities, idForReporting); + } catch (NotEnoughCapabilitiesException e) { + logger.debug("Not enough capabilities: " + e.toString()); + lastException = e; + sleep(); } - Set allNotFound = computeNotFoundCapabilities(expectedCaps, latestCapabilities); - logger.debug("Netconf server did not provide required capabilities. Attempt {}. " + - "Expected but not found: {}, all expected {}, current {}", - attempt, allNotFound, expectedCaps, latestCapabilities); - Util.closeClientAndDispatcher(netconfClient); - Thread.sleep(configuration.connectionAttemptDelayMs); + } while (stopwatch.elapsed(TimeUnit.MILLISECONDS) < maxWaitForCapabilitiesMillis); + throw new IllegalStateException("Max wait for capabilities reached." + lastException.getMessage(), lastException); + } + + private static class NotEnoughCapabilitiesException extends Exception { + private NotEnoughCapabilitiesException(String message) { + super(message); } - if (latestCapabilities == null) { - logger.error("Could not connect to the server in {} ms", configuration.netconfCapabilitiesWaitTimeoutMs); - throw new RuntimeException("Could not connect to netconf server"); + } + + /** + * Get NetconfOperationService iif all required capabilities are present. + * + * @param expectedCapabilities that must be provided by configNetconfConnector + * @param idForReporting + * @return service if capabilities are present, otherwise absent value + */ + private NetconfOperationService getOperationService(Set expectedCapabilities, String idForReporting) throws NotEnoughCapabilitiesException { + NetconfOperationService serviceCandidate = configNetconfConnector.createService(idForReporting); + Set notFoundDiff = computeNotFoundCapabilities(expectedCapabilities, serviceCandidate); + if (notFoundDiff.isEmpty()) { + return serviceCandidate; + } else { + serviceCandidate.close(); + logger.debug("Netconf server did not provide required capabilities for {} " + + "Expected but not found: {}, all expected {}, current {}", + idForReporting, notFoundDiff, expectedCapabilities, serviceCandidate.getCapabilities() + ); + throw new NotEnoughCapabilitiesException("Not enough capabilities for " + idForReporting + ". Expected but not found: " + notFoundDiff); } - Set allNotFound = computeNotFoundCapabilities(expectedCaps, latestCapabilities); - logger.error("Netconf server did not provide required capabilities. Expected but not found: {}, all expected {}, current {}", - allNotFound, expectedCaps, latestCapabilities); - throw new RuntimeException("Netconf server did not provide required capabilities. Expected but not found:" + allNotFound); } - private static Set computeNotFoundCapabilities(Set expectedCaps, Set latestCapabilities) { - Set allNotFound = new HashSet<>(expectedCaps); - allNotFound.removeAll(latestCapabilities); + private static Set computeNotFoundCapabilities(Set expectedCapabilities, NetconfOperationService serviceCandidate) { + Collection actual = Collections2.transform(serviceCandidate.getCapabilities(), new Function() { + @Override + public String apply(Capability input) { + return input.getCapabilityUri(); + } + }); + Set allNotFound = new HashSet<>(expectedCapabilities); + allNotFound.removeAll(actual); return allNotFound; } + + private void sleep() { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IllegalStateException(e); + } + } + /** * Sends two RPCs to the netconf server: edit-config and commit. * * @param configSnapshotHolder - * @param netconfClient * @throws ConflictingVersionException if commit fails on optimistic lock failure inside of config-manager * @throws java.lang.RuntimeException if edit-config or commit fails otherwise */ - private synchronized EditAndCommitResponse pushLastConfig(ConfigSnapshotHolder configSnapshotHolder, NetconfClient netconfClient) + private synchronized EditAndCommitResponse pushConfig(ConfigSnapshotHolder configSnapshotHolder, NetconfOperationService operationService) throws ConflictingVersionException { Element xmlToBePersisted; @@ -177,56 +177,66 @@ public class ConfigPusher { throw new IllegalStateException("Cannot parse " + configSnapshotHolder); } logger.trace("Pushing last configuration to netconf: {}", configSnapshotHolder); - + Stopwatch stopwatch = new Stopwatch().start(); NetconfMessage editConfigMessage = createEditConfigMessage(xmlToBePersisted); - // sending message to netconf - NetconfMessage editResponseMessage; - try { - editResponseMessage = sendRequestGetResponseCheckIsOK(editConfigMessage, netconfClient); - } catch (IOException e) { - throw new IllegalStateException("Edit-config failed on " + configSnapshotHolder, e); - } + Document editResponseMessage = sendRequestGetResponseCheckIsOK(editConfigMessage, operationService, + "edit-config", configSnapshotHolder.toString()); - // commit - NetconfMessage commitResponseMessage; - try { - commitResponseMessage = sendRequestGetResponseCheckIsOK(getCommitMessage(), netconfClient); - } catch (IOException e) { - throw new IllegalStateException("Edit commit succeeded, but commit failed on " + configSnapshotHolder, e); - } + Document commitResponseMessage = sendRequestGetResponseCheckIsOK(getCommitMessage(), operationService, + "commit", configSnapshotHolder.toString()); if (logger.isTraceEnabled()) { StringBuilder response = new StringBuilder("editConfig response = {"); - response.append(XmlUtil.toString(editResponseMessage.getDocument())); + response.append(XmlUtil.toString(editResponseMessage)); response.append("}"); response.append("commit response = {"); - response.append(XmlUtil.toString(commitResponseMessage.getDocument())); + response.append(XmlUtil.toString(commitResponseMessage)); response.append("}"); logger.trace("Last configuration loaded successfully"); logger.trace("Detailed message {}", response); + logger.trace("Total time spent {} ms", stopwatch.elapsed(TimeUnit.MILLISECONDS)); } return new EditAndCommitResponse(editResponseMessage, commitResponseMessage); } + private NetconfOperation findOperation(NetconfMessage request, NetconfOperationService operationService) { + TreeMap allOperations = new TreeMap<>(); + Set netconfOperations = operationService.getNetconfOperations(); + if (netconfOperations.isEmpty()) { + throw new IllegalStateException("Possible code error: no config operations"); + } + for (NetconfOperation netconfOperation : netconfOperations) { + HandlingPriority handlingPriority = netconfOperation.canHandle(request.getDocument()); + allOperations.put(handlingPriority, netconfOperation); + } + Entry highestEntry = allOperations.lastEntry(); + if (highestEntry.getKey().isCannotHandle()) { + throw new IllegalStateException("Possible code error: operation with highest priority is CANNOT_HANDLE"); + } + return highestEntry.getValue(); + } + + private Document sendRequestGetResponseCheckIsOK(NetconfMessage request, NetconfOperationService operationService, + String operationNameForReporting, String configIdForReporting) + throws ConflictingVersionException { - private NetconfMessage sendRequestGetResponseCheckIsOK(NetconfMessage request, NetconfClient netconfClient) - throws ConflictingVersionException, IOException { + NetconfOperation operation = findOperation(request, operationService); + Document response; try { - NetconfMessage netconfMessage = netconfClient.sendMessage(request, - configuration.netconfSendMessageMaxAttempts, configuration.netconfSendMessageDelayMs); - NetconfUtil.checkIsMessageOk(netconfMessage); - return netconfMessage; - } catch(ConflictingVersionException e) { - logger.trace("conflicting version detected: {}", e.toString()); + response = operation.handle(request.getDocument(), NetconfOperationChainedExecution.EXECUTION_TERMINATION_POINT); + } catch (NetconfDocumentedException | RuntimeException e) { + throw new IllegalStateException("Failed to send " + operationNameForReporting + + " for configuration " + configIdForReporting, e); + } + try { + return NetconfUtil.checkIsMessageOk(response); + } catch (ConflictingVersionException e) { + logger.trace("conflicting version detected: {} while committing {}", e.toString(), configIdForReporting); throw e; - } catch (RuntimeException | ExecutionException | InterruptedException | TimeoutException e) { // TODO: change NetconfClient#sendMessage to throw checked exceptions - logger.debug("Error while executing netconf transaction {} to {}", request, netconfClient, e); - throw new IOException("Failed to execute netconf transaction", e); } } - // load editConfig.xml template, populate /rpc/edit-config/config with parameter private static NetconfMessage createEditConfigMessage(Element dataElement) { String editConfigResourcePath = "/netconfOp/editConfig.xml"; @@ -246,7 +256,7 @@ public class ConfigPusher { return new NetconfMessage(doc); } catch (IOException | SAXException e) { // error reading the xml file bundled into the jar - throw new RuntimeException("Error while opening local resource " + editConfigResourcePath, e); + throw new IllegalStateException("Error while opening local resource " + editConfigResourcePath, e); } } @@ -257,23 +267,23 @@ public class ConfigPusher { return new NetconfMessage(XmlUtil.readXmlToDocument(stream)); } catch (SAXException | IOException e) { // error reading the xml file bundled into the jar - throw new RuntimeException("Error while opening local resource " + resource, e); + throw new IllegalStateException("Error while opening local resource " + resource, e); } } static class EditAndCommitResponse { - private final NetconfMessage editResponse, commitResponse; + private final Document editResponse, commitResponse; - EditAndCommitResponse(NetconfMessage editResponse, NetconfMessage commitResponse) { + EditAndCommitResponse(Document editResponse, Document commitResponse) { this.editResponse = editResponse; this.commitResponse = commitResponse; } - public NetconfMessage getEditResponse() { + public Document getEditResponse() { return editResponse; } - public NetconfMessage getCommitResponse() { + public Document getCommitResponse() { return commitResponse; } @@ -285,32 +295,4 @@ public class ConfigPusher { '}'; } } - - - static class EditAndCommitResponseWithRetries { - private final EditAndCommitResponse editAndCommitResponse; - private final int retries; - - EditAndCommitResponseWithRetries(EditAndCommitResponse editAndCommitResponse, int retries) { - this.editAndCommitResponse = editAndCommitResponse; - this.retries = retries; - } - - public int getRetries() { - return retries; - } - - public EditAndCommitResponse getEditAndCommitResponse() { - return editAndCommitResponse; - } - - @Override - public String toString() { - return "EditAndCommitResponseWithRetries{" + - "editAndCommitResponse=" + editAndCommitResponse + - ", retries=" + retries + - '}'; - } - } - } diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusherConfiguration.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusherConfiguration.java deleted file mode 100644 index aa189f06b4..0000000000 --- a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusherConfiguration.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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.persist.impl; - -import io.netty.channel.EventLoopGroup; - -import javax.annotation.concurrent.Immutable; -import java.net.InetSocketAddress; -import java.util.concurrent.TimeUnit; - -/** - * Configuration properties for ConfigPusher. Contains delays and timeouts for netconf - * connection establishment, netconf capabilities stabilization and configuration push. - */ -@Immutable -public final class ConfigPusherConfiguration { - - public static final long DEFAULT_CONNECTION_ATTEMPT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5); - public static final int DEFAULT_CONNECTION_ATTEMPT_DELAY_MS = 5000; - - public static final int DEFAULT_NETCONF_SEND_MESSAGE_MAX_ATTEMPTS = 20; - public static final int DEFAULT_NETCONF_SEND_MESSAGE_DELAY_MS = 1000; - - public static final long DEFAULT_NETCONF_CAPABILITIES_WAIT_TIMEOUT_MS = TimeUnit.MINUTES.toMillis(2); - - public static final int DEFAULT_NETCONF_PUSH_CONFIG_ATTEMPTS = 30; - public static final long DEFAULT_NETCONF_PUSH_CONFIG_DELAY_MS = TimeUnit.MINUTES.toMillis(1); - - final InetSocketAddress netconfAddress; - final EventLoopGroup eventLoopGroup; - - /** - * Total time to wait for capability stabilization - */ - final long netconfCapabilitiesWaitTimeoutMs; - - /** - * Delay between message send attempts - */ - final int netconfSendMessageDelayMs; - /** - * Total number attempts to send a message - */ - final int netconfSendMessageMaxAttempts; - - /** - * Delay between connection establishment attempts - */ - final int connectionAttemptDelayMs; - /** - * Total number of attempts to perform connection establishment - */ - final long connectionAttemptTimeoutMs; - - /** - * Total number of attempts to push configuration to netconf - */ - final int netconfPushConfigAttempts; - /** - * Delay between configuration push attempts - */ - final long netconfPushConfigDelayMs; - - ConfigPusherConfiguration(InetSocketAddress netconfAddress, long netconfCapabilitiesWaitTimeoutMs, - int netconfSendMessageDelayMs, int netconfSendMessageMaxAttempts, int connectionAttemptDelayMs, - long connectionAttemptTimeoutMs, EventLoopGroup eventLoopGroup, int netconfPushConfigAttempts, - long netconfPushConfigDelayMs) { - this.netconfAddress = netconfAddress; - this.netconfCapabilitiesWaitTimeoutMs = netconfCapabilitiesWaitTimeoutMs; - this.netconfSendMessageDelayMs = netconfSendMessageDelayMs; - this.netconfSendMessageMaxAttempts = netconfSendMessageMaxAttempts; - this.connectionAttemptDelayMs = connectionAttemptDelayMs; - this.connectionAttemptTimeoutMs = connectionAttemptTimeoutMs; - this.eventLoopGroup = eventLoopGroup; - this.netconfPushConfigAttempts = netconfPushConfigAttempts; - this.netconfPushConfigDelayMs = netconfPushConfigDelayMs; - } -} diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusherConfigurationBuilder.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusherConfigurationBuilder.java deleted file mode 100644 index c26dc8dbe1..0000000000 --- a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/ConfigPusherConfigurationBuilder.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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.persist.impl; - -import io.netty.channel.EventLoopGroup; - -import java.net.InetSocketAddress; - -public class ConfigPusherConfigurationBuilder { - InetSocketAddress netconfAddress; - EventLoopGroup eventLoopGroup; - - long netconfCapabilitiesWaitTimeoutMs = ConfigPusherConfiguration.DEFAULT_NETCONF_CAPABILITIES_WAIT_TIMEOUT_MS; - int netconfSendMessageDelayMs = ConfigPusherConfiguration.DEFAULT_NETCONF_SEND_MESSAGE_DELAY_MS; - int netconfSendMessageMaxAttempts = ConfigPusherConfiguration.DEFAULT_NETCONF_SEND_MESSAGE_MAX_ATTEMPTS; - int connectionAttemptDelayMs = ConfigPusherConfiguration.DEFAULT_CONNECTION_ATTEMPT_DELAY_MS; - long connectionAttemptTimeoutMs = ConfigPusherConfiguration.DEFAULT_CONNECTION_ATTEMPT_TIMEOUT_MS; - int netconfPushConfigAttempts = ConfigPusherConfiguration.DEFAULT_NETCONF_PUSH_CONFIG_ATTEMPTS; - long netconfPushConfigDelayMs = ConfigPusherConfiguration.DEFAULT_NETCONF_PUSH_CONFIG_DELAY_MS; - - private ConfigPusherConfigurationBuilder() { - } - - public static ConfigPusherConfigurationBuilder aConfigPusherConfiguration() { - return new ConfigPusherConfigurationBuilder(); - } - - public ConfigPusherConfigurationBuilder withNetconfAddress(InetSocketAddress netconfAddress) { - this.netconfAddress = netconfAddress; - return this; - } - - public ConfigPusherConfigurationBuilder withNetconfCapabilitiesWaitTimeoutMs(long netconfCapabilitiesWaitTimeoutMs) { - this.netconfCapabilitiesWaitTimeoutMs = netconfCapabilitiesWaitTimeoutMs; - return this; - } - - public ConfigPusherConfigurationBuilder withNetconfSendMessageDelayMs(int netconfSendMessageDelayMs) { - this.netconfSendMessageDelayMs = netconfSendMessageDelayMs; - return this; - } - - public ConfigPusherConfigurationBuilder withNetconfSendMessageMaxAttempts(int netconfSendMessageMaxAttempts) { - this.netconfSendMessageMaxAttempts = netconfSendMessageMaxAttempts; - return this; - } - - public ConfigPusherConfigurationBuilder withConnectionAttemptDelayMs(int connectionAttemptDelayMs) { - this.connectionAttemptDelayMs = connectionAttemptDelayMs; - return this; - } - - public ConfigPusherConfigurationBuilder withConnectionAttemptTimeoutMs(long connectionAttemptTimeoutMs) { - this.connectionAttemptTimeoutMs = connectionAttemptTimeoutMs; - return this; - } - - public ConfigPusherConfigurationBuilder withEventLoopGroup(EventLoopGroup eventLoopGroup) { - this.eventLoopGroup = eventLoopGroup; - return this; - } - - public ConfigPusherConfigurationBuilder withNetconfPushConfigAttempts(int netconfPushConfigAttempts) { - this.netconfPushConfigAttempts = netconfPushConfigAttempts; - return this; - } - - public ConfigPusherConfigurationBuilder withNetconfPushConfigDelayMs(long netconfPushConfigDelayMs) { - this.netconfPushConfigDelayMs = netconfPushConfigDelayMs; - return this; - } - - public ConfigPusherConfiguration build() { - ConfigPusherConfiguration configPusherConfiguration = new ConfigPusherConfiguration(netconfAddress, - netconfCapabilitiesWaitTimeoutMs, netconfSendMessageDelayMs, netconfSendMessageMaxAttempts, - connectionAttemptDelayMs, connectionAttemptTimeoutMs, eventLoopGroup, netconfPushConfigAttempts, - netconfPushConfigDelayMs); - return configPusherConfiguration; - } -} diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/Util.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/Util.java deleted file mode 100644 index 322a9b753b..0000000000 --- a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/Util.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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.persist.impl; - -import org.opendaylight.controller.netconf.client.NetconfClient; -import org.opendaylight.controller.netconf.client.NetconfClientDispatcher; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.Set; - -public final class Util { - private static final Logger logger = LoggerFactory.getLogger(Util.class); - - - public static boolean isSubset(NetconfClient netconfClient, Set expectedCaps) { - return isSubset(netconfClient.getCapabilities(), expectedCaps); - - } - - private static boolean isSubset(Set currentCapabilities, Set expectedCaps) { - for (String exCap : expectedCaps) { - if (currentCapabilities.contains(exCap) == false) - return false; - } - return true; - } - - public static void closeClientAndDispatcher(NetconfClient client) { - NetconfClientDispatcher dispatcher = client.getNetconfClientDispatcher(); - Exception fromClient = null; - try { - client.close(); - } catch (Exception e) { - fromClient = e; - } finally { - try { - dispatcher.close(); - } catch (Exception e) { - if (fromClient != null) { - e.addSuppressed(fromClient); - } - throw new RuntimeException("Error closing temporary client ", e); - } - } - } -} diff --git a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/osgi/ConfigPersisterActivator.java b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/osgi/ConfigPersisterActivator.java index 1246c78fbe..76afe8eb39 100644 --- a/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/osgi/ConfigPersisterActivator.java +++ b/opendaylight/netconf/config-persister-impl/src/main/java/org/opendaylight/controller/netconf/persist/impl/osgi/ConfigPersisterActivator.java @@ -9,148 +9,139 @@ package org.opendaylight.controller.netconf.persist.impl.osgi; import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Optional; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.nio.NioEventLoopGroup; +import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder; +import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory; import org.opendaylight.controller.netconf.persist.impl.ConfigPersisterNotificationHandler; import org.opendaylight.controller.netconf.persist.impl.ConfigPusher; -import org.opendaylight.controller.netconf.persist.impl.ConfigPusherConfiguration; -import org.opendaylight.controller.netconf.persist.impl.ConfigPusherConfigurationBuilder; import org.opendaylight.controller.netconf.persist.impl.PersisterAggregator; -import org.opendaylight.controller.netconf.util.osgi.NetconfConfigUtil; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.framework.Filter; +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; import javax.management.MBeanServer; import java.lang.management.ManagementFactory; -import java.net.InetSocketAddress; -import java.util.concurrent.ThreadFactory; -import java.util.regex.Pattern; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; public class ConfigPersisterActivator implements BundleActivator { private static final Logger logger = LoggerFactory.getLogger(ConfigPersisterActivator.class); - public static final String IGNORED_MISSING_CAPABILITY_REGEX_SUFFIX = "ignoredMissingCapabilityRegex"; - - public static final String MAX_WAIT_FOR_CAPABILITIES_MILLIS = "maxWaitForCapabilitiesMillis"; + public static final String MAX_WAIT_FOR_CAPABILITIES_MILLIS_PROPERTY = "maxWaitForCapabilitiesMillis"; + private static final long MAX_WAIT_FOR_CAPABILITIES_MILLIS_DEFAULT = TimeUnit.MINUTES.toMillis(2); + public static final String CONFLICTING_VERSION_TIMEOUT_MILLIS_PROPERTY = "conflictingVersionTimeoutMillis"; + private static final long CONFLICTING_VERSION_TIMEOUT_MILLIS_DEFAULT = TimeUnit.SECONDS.toMillis(30); public static final String NETCONF_CONFIG_PERSISTER = "netconf.config.persister"; public static final String STORAGE_ADAPTER_CLASS_PROP_SUFFIX = "storageAdapterClass"; - public static final String DEFAULT_IGNORED_REGEX = "^urn:ietf:params:xml:ns:netconf:base:1.0"; - private final MBeanServer platformMBeanServer; + private static final MBeanServer platformMBeanServer = ManagementFactory.getPlatformMBeanServer(); - private final Optional initialConfigForPusher; - private volatile ConfigPersisterNotificationHandler jmxNotificationHandler; - private Thread initializationThread; - private ThreadFactory initializationThreadFactory; - private EventLoopGroup nettyThreadGroup; - private PersisterAggregator persisterAggregator; + private List autoCloseables; - public ConfigPersisterActivator() { - this(new ThreadFactory() { - @Override - public Thread newThread(Runnable initializationRunnable) { - return new Thread(initializationRunnable, "ConfigPersister-registrator"); - } - }, ManagementFactory.getPlatformMBeanServer(), null); - } - - @VisibleForTesting - protected ConfigPersisterActivator(ThreadFactory threadFactory, MBeanServer mBeanServer, - ConfigPusherConfiguration initialConfigForPusher) { - this.initializationThreadFactory = threadFactory; - this.platformMBeanServer = mBeanServer; - this.initialConfigForPusher = Optional.fromNullable(initialConfigForPusher); - } @Override public void start(final BundleContext context) throws Exception { logger.debug("ConfigPersister starting"); - + autoCloseables = new ArrayList<>(); PropertiesProviderBaseImpl propertiesProvider = new PropertiesProviderBaseImpl(context); - final Pattern ignoredMissingCapabilityRegex = getIgnoredCapabilitiesProperty(propertiesProvider); - persisterAggregator = PersisterAggregator.createFromProperties(propertiesProvider); + final PersisterAggregator persisterAggregator = PersisterAggregator.createFromProperties(propertiesProvider); + autoCloseables.add(persisterAggregator); + final long maxWaitForCapabilitiesMillis = getMaxWaitForCapabilitiesMillis(propertiesProvider); + final List configs = persisterAggregator.loadLastConfigs(); + final long conflictingVersionTimeoutMillis = getConflictingVersionTimeoutMillis(propertiesProvider); + logger.trace("Following configs will be pushed: {}", configs); + ServiceTrackerCustomizer configNetconfCustomizer = new ServiceTrackerCustomizer() { + @Override + public NetconfOperationServiceFactory addingService(ServiceReference reference) { + NetconfOperationServiceFactory service = reference.getBundle().getBundleContext().getService(reference); + final ConfigPusher configPusher = new ConfigPusher(service, maxWaitForCapabilitiesMillis, conflictingVersionTimeoutMillis); + logger.debug("Configuration Persister got %s", service); + final Thread pushingThread = new Thread(new Runnable() { + @Override + public void run() { + configPusher.pushConfigs(configs); + logger.info("Configuration Persister initialization completed."); + ConfigPersisterNotificationHandler jmxNotificationHandler = new ConfigPersisterNotificationHandler(platformMBeanServer, persisterAggregator); + synchronized (ConfigPersisterActivator.this) { + autoCloseables.add(jmxNotificationHandler); + } + } + }, "config-pusher"); + synchronized (ConfigPersisterActivator.this){ + autoCloseables.add(new AutoCloseable() { + @Override + public void close() throws Exception { + pushingThread.interrupt(); + } + }); + } + pushingThread.start(); + return service; + } - final ConfigPusher configPusher = new ConfigPusher(getConfigurationForPusher(context, propertiesProvider)); + @Override + public void modifiedService(ServiceReference reference, NetconfOperationServiceFactory service) { + } - // offload initialization to another thread in order to stop blocking activator - Runnable initializationRunnable = new Runnable() { @Override - public void run() { - try { - configPusher.pushConfigs(persisterAggregator.loadLastConfigs()); - jmxNotificationHandler = new ConfigPersisterNotificationHandler(platformMBeanServer, persisterAggregator, - ignoredMissingCapabilityRegex); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - logger.error("Interrupted while waiting for netconf connection"); - // uncaught exception handler will deal with this failure - throw new RuntimeException("Interrupted while waiting for netconf connection", e); - } - logger.info("Configuration Persister initialization completed."); + public void removedService(ServiceReference reference, NetconfOperationServiceFactory service) { } }; - initializationThread = initializationThreadFactory.newThread(initializationRunnable); - initializationThread.start(); - } - - private Pattern getIgnoredCapabilitiesProperty(PropertiesProviderBaseImpl propertiesProvider) { - String regexProperty = propertiesProvider.getProperty(IGNORED_MISSING_CAPABILITY_REGEX_SUFFIX); - String regex; - if (regexProperty != null) { - regex = regexProperty; - } else { - regex = DEFAULT_IGNORED_REGEX; - } - return Pattern.compile(regex); - } + Filter filter = context.createFilter(getFilterString()); - private Optional getMaxWaitForCapabilitiesProperty(PropertiesProviderBaseImpl propertiesProvider) { - String timeoutProperty = propertiesProvider.getProperty(MAX_WAIT_FOR_CAPABILITIES_MILLIS); - return Optional.fromNullable(timeoutProperty == null ? null : Long.valueOf(timeoutProperty)); + ServiceTracker tracker = + new ServiceTracker<>(context, filter, configNetconfCustomizer); + tracker.open(); } - private ConfigPusherConfiguration getConfigurationForPusher(BundleContext context, - PropertiesProviderBaseImpl propertiesProvider) { - - // If configuration was injected via constructor, use it - if(initialConfigForPusher.isPresent()) - return initialConfigForPusher.get(); - - Optional maxWaitForCapabilitiesMillis = getMaxWaitForCapabilitiesProperty(propertiesProvider); - final InetSocketAddress address = NetconfConfigUtil.extractTCPNetconfAddress(context, - "Netconf is not configured, persister is not operational", true); - - nettyThreadGroup = new NioEventLoopGroup(); - ConfigPusherConfigurationBuilder configPusherConfigurationBuilder = ConfigPusherConfigurationBuilder.aConfigPusherConfiguration(); + @VisibleForTesting + public static String getFilterString() { + return "(&" + + "(" + Constants.OBJECTCLASS + "=" + NetconfOperationServiceFactory.class.getName() + ")" + + "(name" + "=" + "config-netconf-connector" + ")" + + ")"; + } - if(maxWaitForCapabilitiesMillis.isPresent()) - configPusherConfigurationBuilder.withNetconfCapabilitiesWaitTimeoutMs(maxWaitForCapabilitiesMillis.get()); + private long getConflictingVersionTimeoutMillis(PropertiesProviderBaseImpl propertiesProvider) { + String timeoutProperty = propertiesProvider.getProperty(CONFLICTING_VERSION_TIMEOUT_MILLIS_PROPERTY); + return timeoutProperty == null ? CONFLICTING_VERSION_TIMEOUT_MILLIS_DEFAULT : Long.valueOf(timeoutProperty); + } - return configPusherConfigurationBuilder - .withEventLoopGroup(nettyThreadGroup) - .withNetconfAddress(address) - .build(); + private long getMaxWaitForCapabilitiesMillis(PropertiesProviderBaseImpl propertiesProvider) { + String timeoutProperty = propertiesProvider.getProperty(MAX_WAIT_FOR_CAPABILITIES_MILLIS_PROPERTY); + return timeoutProperty == null ? MAX_WAIT_FOR_CAPABILITIES_MILLIS_DEFAULT : Long.valueOf(timeoutProperty); } @Override - public void stop(BundleContext context) throws Exception { - initializationThread.interrupt(); - if (jmxNotificationHandler != null) { - jmxNotificationHandler.close(); + public synchronized void stop(BundleContext context) throws Exception { + Exception lastException = null; + for (AutoCloseable autoCloseable : autoCloseables) { + try { + autoCloseable.close(); + } catch (Exception e) { + if (lastException == null) { + lastException = e; + } else { + lastException.addSuppressed(e); + } + } + } + if (lastException != null) { + throw lastException; } - if(nettyThreadGroup!=null) - nettyThreadGroup.shutdownGracefully(); - persisterAggregator.close(); } } diff --git a/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/CapabilityStrippingConfigSnapshotHolderTest.java b/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/CapabilityStrippingConfigSnapshotHolderTest.java index d91712f347..7e9d80abc0 100644 --- a/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/CapabilityStrippingConfigSnapshotHolderTest.java +++ b/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/CapabilityStrippingConfigSnapshotHolderTest.java @@ -10,44 +10,30 @@ package org.opendaylight.controller.netconf.persist.impl; import com.google.common.collect.Sets; import org.apache.commons.io.IOUtils; import org.junit.Test; -import org.opendaylight.controller.netconf.persist.impl.osgi.ConfigPersisterActivator; import org.opendaylight.controller.netconf.util.xml.XmlUtil; import org.w3c.dom.Element; import java.io.IOException; -import java.util.Collections; import java.util.HashSet; import java.util.Set; -import java.util.regex.Pattern; import static org.junit.Assert.assertEquals; public class CapabilityStrippingConfigSnapshotHolderTest { @Test - public void testCapabilityStripping() throws Exception { + public void testCapabilityStripping() throws Exception { Set allCapabilities = readLines("/capabilities-all.txt"); Set expectedCapabilities = readLines("/capabilities-stripped.txt"); String snapshotAsString = readToString("/snapshot.xml"); Element element = XmlUtil.readXmlToElement(snapshotAsString); - { - CapabilityStrippingConfigSnapshotHolder tested = new CapabilityStrippingConfigSnapshotHolder( - element, allCapabilities, Pattern.compile( - ConfigPersisterActivator.DEFAULT_IGNORED_REGEX - )); - assertEquals(expectedCapabilities, tested.getCapabilities()); - assertEquals(Collections.emptySet(), tested.getMissingNamespaces()); - } - { - // test regex - CapabilityStrippingConfigSnapshotHolder tested = new CapabilityStrippingConfigSnapshotHolder( - element, allCapabilities, Pattern.compile( - "^bar" - )); - assertEquals(expectedCapabilities, tested.getCapabilities()); - assertEquals(Sets.newHashSet(ConfigPersisterActivator.DEFAULT_IGNORED_REGEX.substring(1)), - tested.getMissingNamespaces()); - } + CapabilityStrippingConfigSnapshotHolder tested = new CapabilityStrippingConfigSnapshotHolder( + element, allCapabilities); + assertEquals(expectedCapabilities, tested.getCapabilities()); + + Set obsoleteCapabilities = Sets.difference(allCapabilities, expectedCapabilities); + + assertEquals(obsoleteCapabilities, tested.getObsoleteCapabilities()); } private Set readLines(String fileName) throws IOException { diff --git a/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/osgi/ConfigPersisterTest.java b/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/osgi/ConfigPersisterTest.java index 230c74725d..b722496142 100644 --- a/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/osgi/ConfigPersisterTest.java +++ b/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/osgi/ConfigPersisterTest.java @@ -7,267 +7,138 @@ */ package org.opendaylight.controller.netconf.persist.impl.osgi; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; - -import java.lang.management.ManagementFactory; -import java.net.InetSocketAddress; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.TimeoutException; - -import javax.management.MBeanServer; - +import com.google.common.collect.Sets; import org.junit.After; -import org.junit.AfterClass; -import org.junit.BeforeClass; +import org.junit.Before; import org.junit.Test; -import org.junit.matchers.JUnitMatchers; import org.opendaylight.controller.config.api.ConflictingVersionException; -import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer; -import org.opendaylight.controller.netconf.persist.impl.ConfigPusherConfiguration; -import org.opendaylight.controller.netconf.persist.impl.ConfigPusherConfigurationBuilder; +import org.opendaylight.controller.netconf.api.NetconfDocumentedException; +import org.opendaylight.controller.netconf.mapping.api.Capability; +import org.opendaylight.controller.netconf.mapping.api.HandlingPriority; +import org.opendaylight.controller.netconf.mapping.api.NetconfOperation; +import org.opendaylight.controller.netconf.mapping.api.NetconfOperationChainedExecution; +import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService; +import org.opendaylight.controller.netconf.persist.impl.osgi.MockedBundleContext.DummyAdapterWithInitialSnapshot; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.xml.sax.SAXException; -import com.google.common.collect.Lists; -import io.netty.channel.nio.NioEventLoopGroup; +import javax.management.MBeanServer; +import java.io.IOException; +import java.lang.management.ManagementFactory; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; public class ConfigPersisterTest { + private static final Logger logger = LoggerFactory.getLogger(ConfigPersisterTest.class); private MockedBundleContext ctx; private ConfigPersisterActivator configPersisterActivator; private static final MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer(); + private TestingExceptionHandler handler; - private static final String NETCONF_ADDRESS = "localhost"; - private static final String NETCONF_PORT = "18383"; - private static NioEventLoopGroup eventLoopGroup; - private void setUpContextAndStartPersister(Thread.UncaughtExceptionHandler exHandler, String requiredCapability, ConfigPusherConfiguration configuration) - throws Exception { - MockedBundleContext.DummyAdapterWithInitialSnapshot.expectedCapability = requiredCapability; - ctx = new MockedBundleContext(NETCONF_ADDRESS, NETCONF_PORT); - configPersisterActivator = new ConfigPersisterActivator(getThreadFactory(exHandler), mBeanServer, - configuration); + private void setUpContextAndStartPersister(String requiredCapability) throws Exception { + DummyAdapterWithInitialSnapshot.expectedCapability = requiredCapability; + ctx = new MockedBundleContext(1000, 1000); + configPersisterActivator = new ConfigPersisterActivator(); configPersisterActivator.start(ctx.getBundleContext()); } - @BeforeClass - public static void setUp() throws Exception { - eventLoopGroup = new NioEventLoopGroup(); + @Before + public void setUp() { + handler = new TestingExceptionHandler(); + Thread.setDefaultUncaughtExceptionHandler(handler); } @After public void tearDown() throws Exception { + Thread.setDefaultUncaughtExceptionHandler(null); configPersisterActivator.stop(ctx.getBundleContext()); } - @AfterClass - public static void closeNettyGroup() throws Exception { - eventLoopGroup.shutdownGracefully(); - } - - @Test - public void testPersisterNetconfNotStarting() throws Exception { - final TestingExceptionHandler handler = new TestingExceptionHandler(); - - setUpContextAndStartPersister(handler, "cap2", getConfiguration(100, 100).build()); - - waitTestToFinish(2000); - - handler.assertException("connect to netconf endpoint", RuntimeException.class, - "Could not connect to netconf server"); - } - @Test public void testPersisterNotAllCapabilitiesProvided() throws Exception { - final TestingExceptionHandler handler = new TestingExceptionHandler(); - ConfigPusherConfiguration cfg = getConfiguration(500, 1000) - .withNetconfCapabilitiesWaitTimeoutMs(1000).build(); - - setUpContextAndStartPersister(handler, "required-cap", cfg); + setUpContextAndStartPersister("required-cap"); + Thread.sleep(2000); + handler.assertException(IllegalStateException.class, "Max wait for capabilities reached.Not enough capabilities " + + "for . Expected but not found: [required-cap]"); - try (MockNetconfEndpoint endpoint = startMockNetconfEndpoint("cap1")) { - - waitTestToFinish(2500); - - handler.assertException("retrieve required capabilities from netconf endpoint", RuntimeException.class, - "Expected but not found:[required-cap]"); - } } @Test - public void testPersisterNoResponseFromNetconfAfterEdit() throws Exception { - final TestingExceptionHandler handler = new TestingExceptionHandler(); - ConfigPusherConfiguration cfg = getConfigurationWithOnePushAttempt(); - - setUpContextAndStartPersister(handler, "cap1", cfg); - - try (MockNetconfEndpoint endpoint = startMockNetconfEndpoint("cap1")) { - - waitTestToFinish(3000); - - handler.assertException("receive response from netconf endpoint", IllegalStateException.class, - "Unable to load", TimeoutException.class, - null, 3); - - assertEquals(1 + 2, endpoint.getReceivedMessages().size()); - assertHelloMessage(endpoint.getReceivedMessages().get(1)); - assertEditMessage(endpoint.getReceivedMessages().get(2)); - } + public void testPersisterSuccessfulPush() throws Exception { + setUpContextAndStartPersister("cap1"); + NetconfOperationService service = getWorkingService(getOKDocument()); + doReturn(service).when(ctx.serviceFactory).createService(anyString()); + Thread.sleep(2000); + assertCannotRegisterAsJMXListener_pushWasSuccessful(); } - private ConfigPusherConfiguration getConfigurationWithOnePushAttempt() { - return getConfiguration(500, 1000) - .withNetconfCapabilitiesWaitTimeoutMs(1000) - .withNetconfPushConfigAttempts(1) - .withNetconfPushConfigDelayMs(100) - .withNetconfSendMessageMaxAttempts(3) - .withNetconfSendMessageDelayMs(500).build(); + // this means pushing of config was successful + public void assertCannotRegisterAsJMXListener_pushWasSuccessful() { + handler.assertException(RuntimeException.class, "Cannot register as JMX listener to netconf"); } - @Test - public void testPersisterSuccessfulPush() throws Exception { - final TestingExceptionHandler handler = new TestingExceptionHandler(); - ConfigPusherConfiguration cfg = getConfigurationForSuccess(); - - setUpContextAndStartPersister(handler, "cap1", cfg); - - try (MockNetconfEndpoint endpoint = startMockNetconfEndpoint("cap1", MockNetconfEndpoint.okMessage, - MockNetconfEndpoint.okMessage)) { + public NetconfOperationService getWorkingService(Document document) throws SAXException, IOException, NetconfDocumentedException { + NetconfOperationService service = mock(NetconfOperationService.class); + Capability capability = mock(Capability.class); + doReturn(Sets.newHashSet(capability)).when(service).getCapabilities(); + doReturn("cap1").when(capability).getCapabilityUri(); - waitTestToFinish(4000); - handler.assertException("register as JMX listener", RuntimeException.class, - "Cannot register as JMX listener to netconf"); - - assertEquals(1 + 3, endpoint.getReceivedMessages().size()); - assertCommitMessage(endpoint.getReceivedMessages().get(3)); - } + NetconfOperation mockedOperation = mock(NetconfOperation.class); + doReturn(Sets.newHashSet(mockedOperation)).when(service).getNetconfOperations(); + doReturn(HandlingPriority.getHandlingPriority(1)).when(mockedOperation).canHandle(any(Document.class)); + doReturn(document).when(mockedOperation).handle(any(Document.class), any(NetconfOperationChainedExecution.class)); + doNothing().when(service).close(); + return service; } - private ConfigPusherConfiguration getConfigurationForSuccess() { - return getConfiguration(500, 1000) - .withNetconfCapabilitiesWaitTimeoutMs(1000) - .withNetconfPushConfigAttempts(3) - .withNetconfPushConfigDelayMs(100) - .withNetconfSendMessageMaxAttempts(3) - .withNetconfSendMessageDelayMs(500).build(); + private Document getOKDocument() throws SAXException, IOException { + return XmlUtil.readXmlToDocument( + "\n" + + "\n" + + "" + ); } + @Test public void testPersisterConflictingVersionException() throws Exception { - final TestingExceptionHandler handler = new TestingExceptionHandler(); - ConfigPusherConfiguration cfg = getConfigurationWithOnePushAttempt(); - - setUpContextAndStartPersister(handler, "cap1", cfg); - - try (MockNetconfEndpoint endpoint = startMockNetconfEndpoint("cap1", MockNetconfEndpoint.okMessage, - MockNetconfEndpoint.conflictingVersionErrorMessage); DefaultCommitNotificationProducer jMXNotifier = startJMXCommitNotifier();) { - - Thread.sleep(4000); - - handler.assertException("register as JMX listener", IllegalStateException.class, - "Maximum attempt count has been reached for pushing", ConflictingVersionException.class, "Optimistic lock failed", 1); - - assertEquals(1 + 3, endpoint.getReceivedMessages().size()); - assertCommitMessage(endpoint.getReceivedMessages().get(3)); - } + setUpContextAndStartPersister("cap1"); + NetconfOperationService service = getWorkingService(getConflictVersionDocument()); + doReturn(service).when(ctx.serviceFactory).createService(anyString()); + Thread.sleep(2000); + handler.assertException(IllegalStateException.class, "Max wait for conflicting version stabilization timeout"); } - @Test - public void testPersisterConflictingVersionExceptionThenSuccess() throws Exception { - final TestingExceptionHandler handler = new TestingExceptionHandler(); - ConfigPusherConfiguration cfg = getConfigurationForSuccess(); - - setUpContextAndStartPersister(handler, "cap1", cfg); - - MockNetconfEndpoint.MessageSequence conflictingMessageSequence = new MockNetconfEndpoint.MessageSequence( - MockNetconfEndpoint.okMessage, MockNetconfEndpoint.conflictingVersionErrorMessage); - MockNetconfEndpoint.MessageSequence okMessageSequence = new MockNetconfEndpoint.MessageSequence( - MockNetconfEndpoint.okMessage, MockNetconfEndpoint.okMessage); - - try (MockNetconfEndpoint endpoint = startMockNetconfEndpoint("cap1", - Lists.newArrayList(conflictingMessageSequence, okMessageSequence)); - DefaultCommitNotificationProducer jMXNotifier = startJMXCommitNotifier()) { - - Thread.sleep(4000); - - handler.assertNoException(); - - assertEquals(1 + 3/*Hello + Edit + Commit*/ + 3/*Hello + Edit + Commit*/, endpoint.getReceivedMessages().size()); - assertCommitMessage(endpoint.getReceivedMessages().get(6)); - } + private Document getConflictVersionDocument() throws SAXException, IOException { + return XmlUtil.readXmlToDocument( + "\n" + + "" + + ConflictingVersionException.class.getCanonicalName() + + "\n" + + "" + ); } @Test - public void testPersisterSuccessfulPushAndSuccessfulJMXRegistration() throws Exception { - final TestingExceptionHandler handler = new TestingExceptionHandler(); - ConfigPusherConfiguration cfg = getConfigurationForSuccess(); - - setUpContextAndStartPersister(handler, "cap1", cfg); - - try (MockNetconfEndpoint endpoint = startMockNetconfEndpoint("cap1", MockNetconfEndpoint.okMessage, - MockNetconfEndpoint.okMessage); DefaultCommitNotificationProducer jMXNotifier = startJMXCommitNotifier()) { - - Thread.sleep(2000); - - handler.assertNoException(); - - assertEquals(1 + 3, endpoint.getReceivedMessages().size()); - } - } - - private ConfigPusherConfigurationBuilder getConfiguration(int connectionAttemptDelayMs, int connectionAttemptTimeoutMs) { - return ConfigPusherConfigurationBuilder.aConfigPusherConfiguration() - .withEventLoopGroup(eventLoopGroup) - .withConnectionAttemptDelayMs(connectionAttemptDelayMs) - .withConnectionAttemptTimeoutMs(connectionAttemptTimeoutMs) - .withNetconfCapabilitiesWaitTimeoutMs(44) - .withNetconfAddress(new InetSocketAddress(NETCONF_ADDRESS, Integer.valueOf(NETCONF_PORT))); + public void testSuccessConflictingVersionException() throws Exception { + setUpContextAndStartPersister("cap1"); + doReturn(getWorkingService(getConflictVersionDocument())).when(ctx.serviceFactory).createService(anyString()); + Thread.sleep(500); + // working service: + logger.info("Switching to working service **"); + doReturn(getWorkingService(getOKDocument())).when(ctx.serviceFactory).createService(anyString()); + Thread.sleep(1000); + assertCannotRegisterAsJMXListener_pushWasSuccessful(); } - private void waitTestToFinish(int i) throws InterruptedException { - Thread.sleep(i); - } - - - private DefaultCommitNotificationProducer startJMXCommitNotifier() { - return new DefaultCommitNotificationProducer(mBeanServer); - } - - private void assertEditMessage(String netconfMessage) { - assertThat(netconfMessage, - JUnitMatchers.containsString(MockedBundleContext.DummyAdapterWithInitialSnapshot.CONFIG_SNAPSHOT)); - } - - private void assertCommitMessage(String netconfMessage) { - assertThat(netconfMessage, JUnitMatchers.containsString("")); - assertThat(netconfMessage, JUnitMatchers.containsString("")); - } - - private MockNetconfEndpoint startMockNetconfEndpoint(String capability, List messageSequences) { - // Add first empty sequence for testing connection created by config persister at startup - messageSequences.add(0, new MockNetconfEndpoint.MessageSequence(Collections.emptyList())); - return new MockNetconfEndpoint(capability, NETCONF_PORT, messageSequences); - } - - private MockNetconfEndpoint startMockNetconfEndpoint(String capability, String... messages) { - return startMockNetconfEndpoint(capability, Lists.newArrayList(new MockNetconfEndpoint.MessageSequence(messages))); - } - - public ThreadFactory getThreadFactory(final Thread.UncaughtExceptionHandler exHandler) { - return new ThreadFactory() { - @Override - public Thread newThread(Runnable r) { - Thread thread = new Thread(r, "config-persister-testing-activator"); - thread.setUncaughtExceptionHandler(exHandler); - return thread; - } - }; - } } diff --git a/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/osgi/MockNetconfEndpoint.java b/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/osgi/MockNetconfEndpoint.java deleted file mode 100644 index 913db280b5..0000000000 --- a/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/osgi/MockNetconfEndpoint.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * 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.persist.impl.osgi; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.PrintWriter; -import java.net.ServerSocket; -import java.net.Socket; -import java.net.SocketTimeoutException; -import java.util.Collection; -import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; - -import org.opendaylight.controller.netconf.util.test.XmlFileLoader; -import org.opendaylight.controller.netconf.util.xml.XmlUtil; - -import com.google.common.collect.Lists; - -class MockNetconfEndpoint implements AutoCloseable { - - public static final int READ_SOCKET_TIMEOUT = 3000; - - public static final String MSG_SEPARATOR = "]]>]]>\n"; - - private final AtomicBoolean stopped = new AtomicBoolean(false); - private List receivedMessages = Lists.newCopyOnWriteArrayList(); - private Thread innerThread; - - MockNetconfEndpoint(String capability, String netconfPort, List messageSequence) { - helloMessage = helloMessage.replace("capability_place_holder", capability); - start(netconfPort, messageSequence); - } - - private String helloMessage = "\n" + - "\n" + - "capability_place_holder\n" + - "\n" + - "1\n" + - "\n" + - MSG_SEPARATOR; - - public static String conflictingVersionErrorMessage; - static { - try { - conflictingVersionErrorMessage = XmlUtil.toString(XmlFileLoader - .xmlFileToDocument("netconfMessages/conflictingversion/conflictingVersionResponse.xml")) + MSG_SEPARATOR; - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - public static String okMessage = "\n" + - "\n" + - "" + - MSG_SEPARATOR ; - - private void start(final String port, final List messagesToSend) { - innerThread = new Thread(new Runnable() { - @Override - public void run() { - int clientCounter = 0; - - while (stopped.get() == false) { - try (ServerSocket s = new ServerSocket(Integer.valueOf(port))) { - s.setSoTimeout(READ_SOCKET_TIMEOUT); - - Socket clientSocket = s.accept(); - clientCounter++; - clientSocket.setSoTimeout(READ_SOCKET_TIMEOUT); - - PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true); - BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); - - // Negotiate - sendMessage(out, helloMessage); - receiveMessage(in); - - // Accept next message (edit-config) - receiveMessage(in); - - for (String message : getMessageSequenceForClient(messagesToSend, clientCounter)) { - sendMessage(out, message); - receiveMessage(in); - } - } catch (SocketTimeoutException e) { - // No more activity on netconf endpoint, close - return; - } catch (Exception e) { - throw new RuntimeException(e); - } - } - } - - private Iterable getMessageSequenceForClient(List messagesToSend, - int clientCounter) { - if (messagesToSend.size() <= clientCounter) { - return messagesToSend.get(messagesToSend.size() - 1).getMessages(); - } else { - return messagesToSend.get(clientCounter - 1).getMessages(); - } - } - - private void receiveMessage(BufferedReader in) throws Exception { - String message = readMessage(in); - if(message == null || message.equals("")) - return; - receivedMessages.add(message); - } - - private String readMessage(BufferedReader in) throws IOException { - int c; - StringBuilder b = new StringBuilder(); - - while((c = in.read()) != -1) { - b.append((char)c); - if(b.toString().endsWith("]]>]]>")) - break; - } - - return b.toString(); - } - - private void sendMessage(PrintWriter out, String message) throws InterruptedException { - out.print(message); - out.flush(); - } - - }); - innerThread.setName("Mocked-netconf-endpoint-inner-thread"); - innerThread.start(); - } - - public List getReceivedMessages() { - return receivedMessages; - } - - public void close() throws IOException, InterruptedException { - stopped.set(true); - innerThread.join(); - } - - static class MessageSequence { - private List messages; - - MessageSequence(List messages) { - this.messages = messages; - } - - MessageSequence(String... messages) { - this(Lists.newArrayList(messages)); - } - - public Collection getMessages() { - return messages; - } - } -} diff --git a/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/osgi/MockedBundleContext.java b/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/osgi/MockedBundleContext.java index 97cf7ecfe7..8bc787bdef 100644 --- a/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/osgi/MockedBundleContext.java +++ b/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/osgi/MockedBundleContext.java @@ -14,41 +14,71 @@ import org.mockito.MockitoAnnotations; import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder; import org.opendaylight.controller.config.persist.api.Persister; import org.opendaylight.controller.config.persist.api.PropertiesProvider; +import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService; +import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory; import org.opendaylight.controller.netconf.persist.impl.DummyAdapter; +import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; +import org.osgi.framework.Filter; +import org.osgi.framework.ServiceListener; +import org.osgi.framework.ServiceReference; import java.io.IOException; +import java.util.Collections; import java.util.List; import java.util.SortedSet; import java.util.TreeSet; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; final class MockedBundleContext { - @Mock private BundleContext context; + @Mock + private Filter filter; + @Mock + private ServiceReference serviceReference; + @Mock + private Bundle bundle; + @Mock + NetconfOperationServiceFactory serviceFactory; + @Mock + private NetconfOperationService service; - MockedBundleContext(String netconfAddress, String netconfPort) { + MockedBundleContext(long maxWaitForCapabilitiesMillis, long conflictingVersionTimeoutMillis) throws Exception { MockitoAnnotations.initMocks(this); - initContext(netconfAddress, netconfPort); + doReturn(null).when(context).getProperty(anyString()); + initContext(maxWaitForCapabilitiesMillis, conflictingVersionTimeoutMillis); + doReturn(filter).when(context).createFilter(ConfigPersisterActivator.getFilterString()); + String filterString = "filter"; + doReturn(filterString).when(filter).toString(); + doNothing().when(context).addServiceListener(any(ServiceListener.class), eq(filterString)); + ServiceReference[] toBeReturned = {serviceReference}; + doReturn(toBeReturned).when(context).getServiceReferences((String) null, filterString); + doReturn(bundle).when(serviceReference).getBundle(); + doReturn(context).when(bundle).getBundleContext(); + doReturn("").when(serviceReference).toString(); + doReturn(serviceFactory).when(context).getService(any(ServiceReference.class)); + doReturn(service).when(serviceFactory).createService(anyString()); + doReturn(Collections.emptySet()).when(service).getCapabilities(); + doNothing().when(service).close(); } public BundleContext getBundleContext() { return context; } - private void initContext(String netconfAddress, String netconfPort) { - initProp(context, ConfigPersisterActivator.IGNORED_MISSING_CAPABILITY_REGEX_SUFFIX, null); - - initPropNoPrefix(context, "netconf.tcp.client.address", netconfAddress); - initPropNoPrefix(context, "netconf.tcp.client.port", netconfPort); - + private void initContext(long maxWaitForCapabilitiesMillis, long conflictingVersionTimeoutMillis) { initProp(context, "active", "1"); initProp(context, "1." + ConfigPersisterActivator.STORAGE_ADAPTER_CLASS_PROP_SUFFIX, DummyAdapterWithInitialSnapshot.class.getName()); initProp(context, "1." + "readonly", "false"); initProp(context, "1." + ".properties.fileStorage", "target/configuration-persister-test/initial/"); - + initProp(context, ConfigPersisterActivator.MAX_WAIT_FOR_CAPABILITIES_MILLIS_PROPERTY, String.valueOf(maxWaitForCapabilitiesMillis)); + initProp(context, ConfigPersisterActivator.CONFLICTING_VERSION_TIMEOUT_MILLIS_PROPERTY, String.valueOf(conflictingVersionTimeoutMillis)); } private void initProp(BundleContext context, String key, String value) { @@ -66,7 +96,7 @@ final class MockedBundleContext { @Override public List loadLastConfigs() throws IOException { - return Lists.newArrayList(getConfigSnapshopt()); + return Lists.newArrayList(getConfigSnapshot()); } @Override @@ -74,7 +104,7 @@ final class MockedBundleContext { return this; } - public ConfigSnapshotHolder getConfigSnapshopt() { + public ConfigSnapshotHolder getConfigSnapshot() { return new ConfigSnapshotHolder() { @Override public String getConfigSnapshot() { diff --git a/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/osgi/TestingExceptionHandler.java b/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/osgi/TestingExceptionHandler.java index d42c15b834..6fb231d847 100644 --- a/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/osgi/TestingExceptionHandler.java +++ b/opendaylight/netconf/config-persister-impl/src/test/java/org/opendaylight/controller/netconf/persist/impl/osgi/TestingExceptionHandler.java @@ -24,6 +24,10 @@ final class TestingExceptionHandler implements Thread.UncaughtExceptionHandler { this.t = e; } + public void assertException(Class exType, String exMessageToContain) { + assertException(exMessageToContain, exType, exMessageToContain); + } + public void assertException(String failMessageSuffix, Class exType, String exMessageToContain) { if(t == null) { fail("Should fail to " + failMessageSuffix); diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationRouterImpl.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationRouterImpl.java index 8d532d45e8..a358514453 100644 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationRouterImpl.java +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationRouterImpl.java @@ -7,12 +7,9 @@ */ package org.opendaylight.controller.netconf.impl.osgi; -import java.util.Collections; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; - +import com.google.common.base.Preconditions; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; import org.opendaylight.controller.netconf.api.NetconfDocumentedException; import org.opendaylight.controller.netconf.api.NetconfOperationRouter; import org.opendaylight.controller.netconf.api.NetconfSession; @@ -33,9 +30,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; -import com.google.common.base.Preconditions; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; public class NetconfOperationRouterImpl implements NetconfOperationRouter { @@ -186,18 +185,6 @@ public class NetconfOperationRouterImpl implements NetconfOperationRouter { return sortedPriority; } - public static final NetconfOperationChainedExecution EXECUTION_TERMINATION_POINT = new NetconfOperationChainedExecution() { - @Override - public boolean isExecutionTermination() { - return true; - } - - @Override - public Document execute(Document requestMessage) throws NetconfDocumentedException { - throw new IllegalStateException("This execution represents the termination point in operation execution and cannot be executed itself"); - } - }; - private static class NetconfOperationExecution implements NetconfOperationChainedExecution { private final NetconfOperation netconfOperation; private NetconfOperationChainedExecution subsequentExecution; diff --git a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceSnapshot.java b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceSnapshot.java index cb4f53257e..5c08505091 100644 --- a/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceSnapshot.java +++ b/opendaylight/netconf/netconf-impl/src/main/java/org/opendaylight/controller/netconf/impl/osgi/NetconfOperationServiceSnapshot.java @@ -8,15 +8,15 @@ package org.opendaylight.controller.netconf.impl.osgi; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - import org.opendaylight.controller.netconf.mapping.api.NetconfOperationService; import org.opendaylight.controller.netconf.mapping.api.NetconfOperationServiceFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + public class NetconfOperationServiceSnapshot implements AutoCloseable { private static final Logger logger = LoggerFactory.getLogger(NetconfOperationServiceSnapshot.class); @@ -27,7 +27,7 @@ public class NetconfOperationServiceSnapshot implements AutoCloseable { Set services = new HashSet<>(); netconfSessionIdForReporting = getNetconfSessionIdForReporting(sessionId); for (NetconfOperationServiceFactory factory : factories) { - services.add(factory.createService(sessionId, netconfSessionIdForReporting)); + services.add(factory.createService(netconfSessionIdForReporting)); } this.services = Collections.unmodifiableSet(services); } diff --git a/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/ConcurrentClientsTest.java b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/ConcurrentClientsTest.java index 07da7f990a..c1a7b1478b 100644 --- a/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/ConcurrentClientsTest.java +++ b/opendaylight/netconf/netconf-impl/src/test/java/org/opendaylight/controller/netconf/impl/ConcurrentClientsTest.java @@ -111,7 +111,7 @@ public class ConcurrentClientsTest { private NetconfOperationServiceFactory mockOpF() { return new NetconfOperationServiceFactory() { @Override - public NetconfOperationService createService(long netconfSessionId, String netconfSessionIdForReporting) { + public NetconfOperationService createService(String netconfSessionIdForReporting) { return new NetconfOperationService() { @Override public Set getCapabilities() { diff --git a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfConfigPersisterITTest.java b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfConfigPersisterITTest.java index 19007cd037..997cae0f7c 100644 --- a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfConfigPersisterITTest.java +++ b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/NetconfConfigPersisterITTest.java @@ -22,13 +22,13 @@ import org.opendaylight.controller.config.manager.impl.factoriesresolver.Hardcod import org.opendaylight.controller.config.persist.api.ConfigSnapshotHolder; import org.opendaylight.controller.config.persist.api.Persister; import org.opendaylight.controller.config.spi.ModuleFactory; -import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreException; import org.opendaylight.controller.netconf.api.NetconfMessage; import org.opendaylight.controller.netconf.api.jmx.CommitJMXNotification; import org.opendaylight.controller.netconf.api.monitoring.NetconfManagementSession; import org.opendaylight.controller.netconf.client.NetconfClient; import org.opendaylight.controller.netconf.client.NetconfClientDispatcher; import org.opendaylight.controller.netconf.confignetconfconnector.osgi.NetconfOperationServiceFactoryImpl; +import org.opendaylight.controller.netconf.confignetconfconnector.osgi.YangStoreException; import org.opendaylight.controller.netconf.impl.DefaultCommitNotificationProducer; import org.opendaylight.controller.netconf.impl.NetconfServerDispatcher; import org.opendaylight.controller.netconf.impl.osgi.NetconfMonitoringServiceImpl; @@ -54,7 +54,6 @@ import java.net.InetSocketAddress; import java.util.Collection; import java.util.List; import java.util.Set; -import java.util.regex.Pattern; import static junit.framework.Assert.assertEquals; import static org.mockito.Matchers.any; @@ -124,7 +123,7 @@ public class NetconfConfigPersisterITTest extends AbstractNetconfConfigTest { try (NetconfClient persisterClient = new NetconfClient("persister", tcpAddress, 4000, clientDispatcher)) { try (ConfigPersisterNotificationHandler configPersisterNotificationHandler = new ConfigPersisterNotificationHandler( - platformMBeanServer, mockedAggregator, Pattern.compile(""))) { + platformMBeanServer, mockedAggregator)) { try (NetconfClient netconfClient = new NetconfClient("client", tcpAddress, 4000, clientDispatcher)) { diff --git a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/pax/IdentityRefNetconfTest.java b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/pax/IdentityRefNetconfTest.java index 5fd9f2fcd1..ee971a65dd 100644 --- a/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/pax/IdentityRefNetconfTest.java +++ b/opendaylight/netconf/netconf-it/src/test/java/org/opendaylight/controller/netconf/it/pax/IdentityRefNetconfTest.java @@ -44,6 +44,7 @@ import static org.opendaylight.controller.test.sal.binding.it.TestHelper.junitAn import static org.opendaylight.controller.test.sal.binding.it.TestHelper.mdSalCoreBundles; import static org.ops4j.pax.exam.CoreOptions.mavenBundle; import static org.ops4j.pax.exam.CoreOptions.options; +import static org.ops4j.pax.exam.CoreOptions.systemPackages; import static org.ops4j.pax.exam.CoreOptions.systemProperty; @RunWith(PaxExam.class) @@ -62,6 +63,7 @@ public class IdentityRefNetconfTest { systemProperty("osgi.console").value("2401"), systemProperty("osgi.bundles.defaultStartLevel").value("4"), systemProperty("pax.exam.osgi.unresolved.fail").value("true"), + systemPackages("sun.nio.ch"), testingModules(), loggingModules(), diff --git a/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/HandlingPriority.java b/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/HandlingPriority.java index 1236138e6c..05122be4d2 100644 --- a/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/HandlingPriority.java +++ b/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/HandlingPriority.java @@ -45,6 +45,10 @@ public class HandlingPriority implements Comparable { return getHandlingPriority(priority + priorityIncrease); } + public boolean isCannotHandle() { + return this.equals(CANNOT_HANDLE); + } + @Override public int compareTo(HandlingPriority o) { if (this == o) diff --git a/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationChainedExecution.java b/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationChainedExecution.java index 2298153017..4013d623bd 100644 --- a/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationChainedExecution.java +++ b/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationChainedExecution.java @@ -27,4 +27,18 @@ public interface NetconfOperationChainedExecution { * Do not execute if this is termination point */ Document execute(Document requestMessage) throws NetconfDocumentedException; + + public static final NetconfOperationChainedExecution EXECUTION_TERMINATION_POINT = new NetconfOperationChainedExecution() { + @Override + public boolean isExecutionTermination() { + return true; + } + + @Override + public Document execute(Document requestMessage) throws NetconfDocumentedException { + throw new IllegalStateException("This execution represents the termination point in operation execution and cannot be executed itself"); + } + }; + + } diff --git a/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationServiceFactory.java b/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationServiceFactory.java index 46b9cd22e0..81401f26ee 100644 --- a/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationServiceFactory.java +++ b/opendaylight/netconf/netconf-mapping-api/src/main/java/org/opendaylight/controller/netconf/mapping/api/NetconfOperationServiceFactory.java @@ -15,6 +15,6 @@ package org.opendaylight.controller.netconf.mapping.api; */ public interface NetconfOperationServiceFactory { - NetconfOperationService createService(long netconfSessionId, String netconfSessionIdForReporting); + NetconfOperationService createService(String netconfSessionIdForReporting); } diff --git a/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/osgi/NetconfMonitoringActivator.java b/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/osgi/NetconfMonitoringActivator.java index 1143231442..de04484d13 100644 --- a/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/osgi/NetconfMonitoringActivator.java +++ b/opendaylight/netconf/netconf-monitoring/src/main/java/org/opendaylight/controller/netconf/monitoring/osgi/NetconfMonitoringActivator.java @@ -46,7 +46,7 @@ public class NetconfMonitoringActivator implements BundleActivator { } @Override - public NetconfOperationService createService(long netconfSessionId, String netconfSessionIdForReporting) { + public NetconfOperationService createService(String netconfSessionIdForReporting) { return operationService; } } diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/NetconfUtil.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/NetconfUtil.java index 796ab91a50..b0884ca2fb 100644 --- a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/NetconfUtil.java +++ b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/NetconfUtil.java @@ -56,13 +56,17 @@ public final class NetconfUtil { return (doc == null) ? null : new NetconfMessage(doc); } - public static void checkIsMessageOk(NetconfMessage responseMessage) throws ConflictingVersionException { - XmlElement element = XmlElement.fromDomDocument(responseMessage.getDocument()); + public static Document checkIsMessageOk(NetconfMessage responseMessage) throws ConflictingVersionException { + return checkIsMessageOk(responseMessage.getDocument()); + } + + public static Document checkIsMessageOk(Document response) throws ConflictingVersionException { + XmlElement element = XmlElement.fromDomDocument(response); Preconditions.checkState(element.getName().equals(XmlNetconfConstants.RPC_REPLY_KEY)); element = element.getOnlyChildElement(); if (element.getName().equals(XmlNetconfConstants.OK)) { - return; + return response; } if (element.getName().equals(XmlNetconfConstants.RPC_ERROR)) { @@ -74,11 +78,11 @@ public final class NetconfUtil { throw new ConflictingVersionException(error); } throw new IllegalStateException("Can not load last configuration, operation failed: " - + XmlUtil.toString(responseMessage.getDocument())); + + XmlUtil.toString(response)); } logger.warn("Can not load last configuration. Operation failed."); throw new IllegalStateException("Can not load last configuration. Operation failed: " - + XmlUtil.toString(responseMessage.getDocument())); + + XmlUtil.toString(response)); } } diff --git a/opendaylight/netconf/pom.xml b/opendaylight/netconf/pom.xml index e7b9a02552..586366f41a 100644 --- a/opendaylight/netconf/pom.xml +++ b/opendaylight/netconf/pom.xml @@ -47,7 +47,6 @@ 5.0.0 2.4.0 1.7.2 - 4.0.10.Final ${project.build.directory}/generated-sources/sal diff --git a/opendaylight/northbound/networkconfiguration/bridgedomain/src/main/java/org/opendaylight/controller/networkconfig/bridgedomain/northbound/BridgeDomainNorthbound.java b/opendaylight/northbound/networkconfiguration/bridgedomain/src/main/java/org/opendaylight/controller/networkconfig/bridgedomain/northbound/BridgeDomainNorthbound.java index 9ef56e5dc4..9ddba67e25 100644 --- a/opendaylight/northbound/networkconfiguration/bridgedomain/src/main/java/org/opendaylight/controller/networkconfig/bridgedomain/northbound/BridgeDomainNorthbound.java +++ b/opendaylight/northbound/networkconfiguration/bridgedomain/src/main/java/org/opendaylight/controller/networkconfig/bridgedomain/northbound/BridgeDomainNorthbound.java @@ -29,6 +29,7 @@ import org.opendaylight.controller.northbound.commons.exception.NotAcceptableExc import org.opendaylight.controller.northbound.commons.exception.ResourceNotFoundException; import org.opendaylight.controller.northbound.commons.exception.ServiceUnavailableException; import org.opendaylight.controller.sal.core.Node; +import org.opendaylight.controller.sal.networkconfig.bridgedomain.BridgeDomainConfigServiceException; import org.opendaylight.controller.sal.networkconfig.bridgedomain.ConfigConstants; import org.opendaylight.controller.sal.networkconfig.bridgedomain.IBridgeDomainConfigService; import org.opendaylight.controller.sal.utils.ServiceHelper; @@ -114,9 +115,7 @@ public class BridgeDomainNorthbound { if (status.getCode().equals(StatusCode.SUCCESS)) { return Response.status(Response.Status.CREATED).build(); } - } catch (Error e) { - throw e; - } catch (Throwable t) { + } catch (BridgeDomainConfigServiceException e) { return Response.status(Response.Status.PRECONDITION_FAILED).build(); } throw new ResourceNotFoundException(status.getDescription()); diff --git a/opendaylight/protocol_plugins/openflow/src/main/java/org/opendaylight/controller/protocol_plugin/openflow/core/internal/SecureMessageReadWriteService.java b/opendaylight/protocol_plugins/openflow/src/main/java/org/opendaylight/controller/protocol_plugin/openflow/core/internal/SecureMessageReadWriteService.java index aa60f91174..f27d30eaae 100644 --- a/opendaylight/protocol_plugins/openflow/src/main/java/org/opendaylight/controller/protocol_plugin/openflow/core/internal/SecureMessageReadWriteService.java +++ b/opendaylight/protocol_plugins/openflow/src/main/java/org/opendaylight/controller/protocol_plugin/openflow/core/internal/SecureMessageReadWriteService.java @@ -54,6 +54,12 @@ public class SecureMessageReadWriteService implements IMessageReadWrite { // switch private ByteBuffer peerNetData; // encrypted message from the switch private FileInputStream kfd = null, tfd = null; + private final String keyStoreFileDefault = "./configuration/tlsKeyStore"; + private final String trustStoreFileDefault = "./configuration/tlsTrustStore"; + private final String keyStorePasswordPropName = "controllerKeyStorePassword"; + private final String trustStorePasswordPropName = "controllerTrustStorePassword"; + private static String keyStorePassword = null; + private static String trustStorePassword = null; public SecureMessageReadWriteService(SocketChannel socket, Selector selector) throws Exception { @@ -80,32 +86,44 @@ public class SecureMessageReadWriteService implements IMessageReadWrite { */ private void createSecureChannel(SocketChannel socket) throws Exception { String keyStoreFile = System.getProperty("controllerKeyStore"); - String keyStorePassword = System - .getProperty("controllerKeyStorePassword"); String trustStoreFile = System.getProperty("controllerTrustStore"); - String trustStorePassword = System - .getProperty("controllerTrustStorePassword"); + String keyStorePasswordProp = System.getProperty(keyStorePasswordPropName); + String trustStorePasswordProp = System.getProperty(trustStorePasswordPropName); if (keyStoreFile != null) { keyStoreFile = keyStoreFile.trim(); + } else { + keyStoreFile = keyStoreFileDefault; } if ((keyStoreFile == null) || keyStoreFile.isEmpty()) { throw new FileNotFoundException("TLS KeyStore file not found."); } + + if ((keyStorePassword == null) || ((keyStorePasswordProp != null) && !keyStorePasswordProp.isEmpty())) { + keyStorePassword = keyStorePasswordProp; + } if (keyStorePassword != null) { keyStorePassword = keyStorePassword.trim(); + System.setProperty(keyStorePasswordPropName, ""); } if ((keyStorePassword == null) || keyStorePassword.isEmpty()) { throw new FileNotFoundException("TLS KeyStore Password not provided."); } if (trustStoreFile != null) { trustStoreFile = trustStoreFile.trim(); + } else { + trustStoreFile = trustStoreFileDefault; } if ((trustStoreFile == null) || trustStoreFile.isEmpty()) { throw new FileNotFoundException("TLS TrustStore file not found"); } + + if ((trustStorePassword == null) || ((trustStorePasswordProp != null) && !trustStorePasswordProp.isEmpty())) { + trustStorePassword = trustStorePasswordProp; + } if (trustStorePassword != null) { trustStorePassword = trustStorePassword.trim(); + System.setProperty(trustStorePasswordPropName, ""); } if ((trustStorePassword == null) || trustStorePassword.isEmpty()) { throw new FileNotFoundException("TLS TrustStore Password not provided."); diff --git a/opendaylight/sal/networkconfiguration/api/src/main/java/org/opendaylight/controller/sal/networkconfig/bridgedomain/BridgeDomainConfigServiceException.java b/opendaylight/sal/networkconfiguration/api/src/main/java/org/opendaylight/controller/sal/networkconfig/bridgedomain/BridgeDomainConfigServiceException.java new file mode 100644 index 0000000000..19f467ea55 --- /dev/null +++ b/opendaylight/sal/networkconfiguration/api/src/main/java/org/opendaylight/controller/sal/networkconfig/bridgedomain/BridgeDomainConfigServiceException.java @@ -0,0 +1,24 @@ +/* + * 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.sal.networkconfig.bridgedomain; + +/** + * Exception thrown by IPluginInBridgeDomainConfigService implementations. + */ +public class BridgeDomainConfigServiceException extends Exception { + private static final long serialVersionUID = 1L; + + public BridgeDomainConfigServiceException(String message) { + super(message); + } + + public BridgeDomainConfigServiceException(String message, Throwable cause) { + super(message, cause); + } +} + diff --git a/opendaylight/sal/networkconfiguration/api/src/main/java/org/opendaylight/controller/sal/networkconfig/bridgedomain/IPluginInBridgeDomainConfigService.java b/opendaylight/sal/networkconfiguration/api/src/main/java/org/opendaylight/controller/sal/networkconfig/bridgedomain/IPluginInBridgeDomainConfigService.java index f8696b1cec..c84136115c 100644 --- a/opendaylight/sal/networkconfiguration/api/src/main/java/org/opendaylight/controller/sal/networkconfig/bridgedomain/IPluginInBridgeDomainConfigService.java +++ b/opendaylight/sal/networkconfiguration/api/src/main/java/org/opendaylight/controller/sal/networkconfig/bridgedomain/IPluginInBridgeDomainConfigService.java @@ -29,7 +29,7 @@ public interface IPluginInBridgeDomainConfigService { * @note This method will return false if one or more of the supplied params is not supported by the * protocol plugin that serves the Node. */ - public Status createBridgeDomain(Node node, String bridgeIdentifier, Map params) throws Throwable; + public Status createBridgeDomain(Node node, String bridgeIdentifier, Map params) throws BridgeDomainConfigServiceException; /** * Delete a Bridge Domain diff --git a/opendaylight/sal/networkconfiguration/implementation/src/main/java/org/opendaylight/controller/sal/networkconfig/bridgedomain/internal/BridgeDomainConfigService.java b/opendaylight/sal/networkconfiguration/implementation/src/main/java/org/opendaylight/controller/sal/networkconfig/bridgedomain/internal/BridgeDomainConfigService.java index 64c72115f6..14c5e0d9e7 100644 --- a/opendaylight/sal/networkconfiguration/implementation/src/main/java/org/opendaylight/controller/sal/networkconfig/bridgedomain/internal/BridgeDomainConfigService.java +++ b/opendaylight/sal/networkconfiguration/implementation/src/main/java/org/opendaylight/controller/sal/networkconfig/bridgedomain/internal/BridgeDomainConfigService.java @@ -14,6 +14,7 @@ import java.util.concurrent.ConcurrentMap; import org.opendaylight.controller.sal.core.Node; import org.opendaylight.controller.sal.core.NodeConnector; +import org.opendaylight.controller.sal.networkconfig.bridgedomain.BridgeDomainConfigServiceException; import org.opendaylight.controller.sal.networkconfig.bridgedomain.ConfigConstants; import org.opendaylight.controller.sal.networkconfig.bridgedomain.IBridgeDomainConfigService; import org.opendaylight.controller.sal.networkconfig.bridgedomain.IPluginInBridgeDomainConfigService; @@ -26,7 +27,7 @@ import org.slf4j.LoggerFactory; public class BridgeDomainConfigService implements IBridgeDomainConfigService { protected static final Logger logger = LoggerFactory .getLogger(BridgeDomainConfigService.class); - private ConcurrentMap pluginService = + private final ConcurrentMap pluginService = new ConcurrentHashMap(); void setPluginInService (Map props, IPluginInBridgeDomainConfigService s) { @@ -80,7 +81,7 @@ public class BridgeDomainConfigService implements IBridgeDomainConfigService { @Override public Status createBridgeDomain(Node node, String bridgeIdentifier, Map params) - throws Throwable { + throws BridgeDomainConfigServiceException { if (pluginService != null) { IPluginInBridgeDomainConfigService plugin = this.pluginService.get(node.getType()); if (plugin != null) { diff --git a/opendaylight/web/devices/src/main/java/org/opendaylight/controller/devices/web/Devices.java b/opendaylight/web/devices/src/main/java/org/opendaylight/controller/devices/web/Devices.java index e4bb790676..a9f11fafb5 100644 --- a/opendaylight/web/devices/src/main/java/org/opendaylight/controller/devices/web/Devices.java +++ b/opendaylight/web/devices/src/main/java/org/opendaylight/controller/devices/web/Devices.java @@ -774,7 +774,7 @@ public class Devices implements IDaylightWeb { return result; } - @RequestMapping(value = "/connect/{nodeId}", method = RequestMethod.POST) + @RequestMapping(value = "/connect/{nodeId:.+}", method = RequestMethod.POST) @ResponseBody public Status addNode(HttpServletRequest request, @PathVariable("nodeId") String nodeId, @RequestParam(required = true) String ipAddress, @RequestParam(required = true) String port, @@ -811,7 +811,7 @@ public class Devices implements IDaylightWeb { return new Status(StatusCode.SUCCESS); } - @RequestMapping(value = "/disconnect/{nodeId}", method = RequestMethod.POST) + @RequestMapping(value = "/disconnect/{nodeId:.+}", method = RequestMethod.POST) @ResponseBody public Status removeNode(HttpServletRequest request, @PathVariable("nodeId") String nodeId, @RequestParam(required = true) String nodeType) { diff --git a/opendaylight/web/flows/src/main/java/org/opendaylight/controller/flows/web/Flows.java b/opendaylight/web/flows/src/main/java/org/opendaylight/controller/flows/web/Flows.java index 259ffde4b4..9c3c895916 100644 --- a/opendaylight/web/flows/src/main/java/org/opendaylight/controller/flows/web/Flows.java +++ b/opendaylight/web/flows/src/main/java/org/opendaylight/controller/flows/web/Flows.java @@ -14,11 +14,15 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.TreeMap; import javax.servlet.http.HttpServletRequest; import org.opendaylight.controller.forwardingrulesmanager.FlowConfig; import org.opendaylight.controller.forwardingrulesmanager.IForwardingRulesManager; +import org.opendaylight.controller.sal.action.Action; +import org.opendaylight.controller.sal.action.ActionType; +import org.opendaylight.controller.sal.action.SupportedFlowActions; import org.opendaylight.controller.sal.authorization.Privilege; import org.opendaylight.controller.sal.authorization.UserLevel; import org.opendaylight.controller.sal.core.Description; @@ -212,7 +216,6 @@ public class Flows implements IDaylightWeb { return nodes; } - @RequestMapping(value = "/flow", method = RequestMethod.POST) @ResponseBody public String actionFlow(@RequestParam(required = true) String action, @RequestParam(required = false) String body, @@ -337,6 +340,83 @@ public class Flows implements IDaylightWeb { } } + @RequestMapping(value = "/valid-flows/{nodeId}") + @ResponseBody + public Object getValidActions(HttpServletRequest request, @RequestParam(required = false) String container, + @PathVariable("nodeId") String nodeId) { + String containerName = (container == null) ? GlobalConstants.DEFAULT.toString() : container; + + // Authorization check + String userName = request.getUserPrincipal().getName(); + if (DaylightWebUtil.getContainerPrivilege(userName, containerName, this) != Privilege.WRITE) { + return "Operation not authorized"; + } + + ISwitchManager switchManager = (ISwitchManager) ServiceHelper.getInstance(ISwitchManager.class, containerName, this); + if (switchManager == null) { + return null; + } + + Map result = new TreeMap(); + + Node node = Node.fromString(nodeId); + SupportedFlowActions supportedFlows = (SupportedFlowActions) switchManager.getNodeProp(node, "supportedFlowActions"); + List> actions = supportedFlows.getActions(); + for (Class action : actions) { + if (action.isAssignableFrom(org.opendaylight.controller.sal.action.Drop.class)) { + result.put(ActionType.DROP.toString(), "Drop"); + } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.Loopback.class)) { + result.put(ActionType.LOOPBACK.toString(), "Loopback"); + } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.Flood.class)) { + result.put(ActionType.FLOOD.toString(), "Flood"); + } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.FloodAll.class)) { + result.put(ActionType.FLOOD_ALL.toString(), "Flood All"); + } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.Controller.class)) { + result.put(ActionType.CONTROLLER.toString(), "Controller"); + } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SwPath.class)) { + result.put(ActionType.SW_PATH.toString(), "Software Path"); + } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.HwPath.class)) { + result.put(ActionType.HW_PATH.toString(), "Hardware Path"); + } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.Output.class)) { + result.put(ActionType.OUTPUT.toString(), "Output"); + } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.Enqueue.class)) { + result.put(ActionType.ENQUEUE.toString(), "Enqueue"); + } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetDlSrc.class)) { + result.put(ActionType.SET_DL_SRC.toString(), "Set Datalayer Source Address"); + } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetDlDst.class)) { + result.put(ActionType.SET_DL_DST.toString(), "Set Datalayer Destination Address"); + } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetVlanId.class)) { + result.put(ActionType.SET_VLAN_ID.toString(), "Set VLAN ID"); + } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetVlanPcp.class)) { + result.put(ActionType.SET_VLAN_PCP.toString(), "Set VLAN Priority"); + } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetVlanCfi.class)) { + result.put(ActionType.SET_VLAN_CFI.toString(), "Set VLAN CFI"); + } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.PopVlan.class)) { + result.put(ActionType.POP_VLAN.toString(), "Pop VLAN"); + } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.PushVlan.class)) { + result.put(ActionType.PUSH_VLAN.toString(), "Push VLAN"); + } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetDlType.class)) { + result.put(ActionType.SET_DL_TYPE.toString(), "Set EtherType"); + } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetNwSrc.class)) { + result.put(ActionType.SET_NW_SRC.toString(), "Set Network Source Address"); + } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetNwDst.class)) { + result.put(ActionType.SET_NW_DST.toString(), "Set Network Destination Address"); + } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetNwTos.class)) { + result.put(ActionType.SET_NW_TOS.toString(), "Modify ToS Bits"); + } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetTpSrc.class)) { + result.put(ActionType.SET_TP_SRC.toString(), "Modify Transport Source Port"); + } else if (action.isAssignableFrom(org.opendaylight.controller.sal.action.SetTpDst.class)) { + result.put(ActionType.SET_TP_DST.toString(), "Modify Transport Destination Port"); + } + } + + return result; + } + + private boolean actionCompare(String name, ActionType type) { + return name.equals(type.getId().toLowerCase()); + } + private String getNodeDesc(Node node, ISwitchManager switchManager) { Description desc = (Description) switchManager.getNodeProp(node, Description.propertyName); String description = (desc == null) ? "" : desc.getValue(); diff --git a/opendaylight/web/flows/src/main/resources/js/page.js b/opendaylight/web/flows/src/main/resources/js/page.js index ab8301bf73..6e7fd25e04 100644 --- a/opendaylight/web/flows/src/main/resources/js/page.js +++ b/opendaylight/web/flows/src/main/resources/js/page.js @@ -11,1593 +11,1723 @@ one.f = {}; // specify dashlets and layouts one.f.dashlet = { - flows : { - id : 'flows', - name : 'Flow Entries' - }, - nodes : { - id : 'nodes', - name : 'Nodes' - }, - detail : { - id : 'detail', - name : 'Flow Detail' - } + flows : { + id : 'flows', + name : 'Flow Entries' + }, + nodes : { + id : 'nodes', + name : 'Nodes' + }, + detail : { + id : 'detail', + name : 'Flow Detail' + } }; one.f.menu = { - left : { - top : [ - one.f.dashlet.flows - ], - bottom : [ - one.f.dashlet.nodes - ] - }, - right : { - top : [], - bottom : [ - one.f.dashlet.detail - ] - } + left : { + top : [ + one.f.dashlet.flows + ], + bottom : [ + one.f.dashlet.nodes + ] + }, + right : { + top : [], + bottom : [ + one.f.dashlet.detail + ] + } }; one.f.address = { - root : "/controller/web/flows", - flows : { - main : "/main", - flows : "/node-flows", - nodes : "/node-ports", - flow : "/flow", - modifyFlow : "/modifyFlow", - deleteFlows:"/flow/deleteFlows" - } + root : "/controller/web/flows", + flows : { + main : "/main", + flows : "/node-flows", + nodes : "/node-ports", + flow : "/flow", + modifyFlow : "/modifyFlow", + deleteFlows:"/flow/deleteFlows" + } } /** NODES **/ one.f.nodes = { - id : { - dashlet: { - datagrid: "one_f_nodes_id_dashlet_datagrid" - } - }, - registry : {}, - dashlet : function($dashlet) { - var $h4 = one.lib.dashlet.header("Nodes"); - $dashlet.append($h4); - - one.f.nodes.ajax.dashlet(function(data) { - var $gridHTML = one.lib.dashlet.datagrid.init(one.f.nodes.id.dashlet.datagrid, { - searchable: true, - filterable: false, - pagination: true, - flexibleRowsPerPage: true - }, "table-striped table-condensed"); - $dashlet.append($gridHTML); - var dataSource = one.f.nodes.data.nodesDataGrid(data); - $("#" + one.f.nodes.id.dashlet.datagrid).datagrid({dataSource: dataSource}); - }); - }, - ajax : { - dashlet : function(callback) { - $.getJSON(one.f.address.root+one.f.address.flows.flows, function(data) { - callback(data); - }); - } - }, - data : { - nodesDataGrid: function(data) { - var gridData = []; - $.each(data, function(nodeName, flow) { - var nodeFlowObject = {}; - nodeFlowObject["nodeName"] = nodeName; - nodeFlowObject["flows"] = flow; - nodeFlowObject["rowData"] = nodeName + flow + "-foobar"; - gridData.push(nodeFlowObject); - }); + id : { + dashlet: { + datagrid: "one_f_nodes_id_dashlet_datagrid" + } + }, + registry : {}, + dashlet : function($dashlet) { + var $h4 = one.lib.dashlet.header("Nodes"); + $dashlet.append($h4); - var source = new StaticDataSource({ - columns: [ - { - property: 'nodeName', - label: 'Node', - sortable: true - }, - { - property: 'flows', - label: 'Flows', - sortable: true - } - ], - data: gridData, - delay: 0 - }); - return source; - } - }, - body : { - dashlet : function(body, callback) { - var attributes = ['table-striped', 'table-bordered', 'table-hover', 'table-condensed']; - var $table = one.lib.dashlet.table.table(attributes); + one.f.nodes.ajax.dashlet(function(data) { + var $gridHTML = one.lib.dashlet.datagrid.init(one.f.nodes.id.dashlet.datagrid, { + searchable: true, + filterable: false, + pagination: true, + flexibleRowsPerPage: true + }, "table-striped table-condensed"); + $dashlet.append($gridHTML); + var dataSource = one.f.nodes.data.nodesDataGrid(data); + $("#" + one.f.nodes.id.dashlet.datagrid).datagrid({dataSource: dataSource}); + }); + }, + ajax : { + dashlet : function(callback) { + $.getJSON(one.f.address.root+one.f.address.flows.flows, function(data) { + callback(data); + }); + } + }, + data : { + nodesDataGrid: function(data) { + var gridData = []; + $.each(data, function(nodeName, flow) { + var nodeFlowObject = {}; + nodeFlowObject["nodeName"] = nodeName; + nodeFlowObject["flows"] = flow; + nodeFlowObject["rowData"] = nodeName + flow + "-foobar"; + gridData.push(nodeFlowObject); + }); + + var source = new StaticDataSource({ + columns: [ + { + property: 'nodeName', + label: 'Node', + sortable: true + }, + { + property: 'flows', + label: 'Flows', + sortable: true + } + ], + data: gridData, + delay: 0 + }); + return source; + } + }, + body : { + dashlet : function(body, callback) { + var attributes = ['table-striped', 'table-bordered', 'table-hover', 'table-condensed']; + var $table = one.lib.dashlet.table.table(attributes); - var headers = ['Node', 'Flows']; - var $thead = one.lib.dashlet.table.header(headers); - $table.append($thead); + var headers = ['Node', 'Flows']; + var $thead = one.lib.dashlet.table.header(headers); + $table.append($thead); - var $tbody = one.lib.dashlet.table.body(body); - $table.append($tbody); + var $tbody = one.lib.dashlet.table.body(body); + $table.append($tbody); - return $table; - } + return $table; } + } } /** FLOW DETAIL **/ one.f.detail = { - id : {}, - registry : {}, - dashlet : function($dashlet, details) { - var $h4 = one.lib.dashlet.header("Flow Details"); - $dashlet.append($h4); - - // details - if (details == undefined) { - var $none = $(document.createElement('div')); - $none.addClass('none'); - var $p = $(document.createElement('p')); - $p.text('Please select a flow'); - $p.addClass('text-center').addClass('text-info'); + id : {}, + registry : {}, + dashlet : function($dashlet, details) { + var $h4 = one.lib.dashlet.header("Flow Details"); + $dashlet.append($h4); - $dashlet.append($none) - .append($p); - } - }, - data : { - dashlet : function(data) { - var body = []; - var tr = {}; - var entry = []; - - entry.push(data['name']); - entry.push(data['node']); - entry.push(data['flow']['priority']); - entry.push(data['flow']['hardTimeout']); - entry.push(data['flow']['idleTimeout']); - - tr.entry = entry; - body.push(tr); - return body; - }, - description : function(data) { - var body = []; - var tr = {}; - var entry = []; - entry.push(data['flow']['ingressPort']); - entry.push(data['flow']['etherType']); - entry.push(data['flow']['vlanId']); - entry.push(data['flow']['vlanPriority']); - entry.push(data['flow']['srcMac']); - entry.push(data['flow']['dstMac']); - entry.push(data['flow']['srcIp']); - entry.push(data['flow']['dstIp']); - entry.push(data['flow']['tosBits']); - entry.push(data['flow']['srcPort']); - entry.push(data['flow']['dstPort']); - entry.push(data['flow']['protocol']); - entry.push(data['flow']['cookie']); - - tr.entry = entry; - body.push(tr); - return body; - }, - actions : function(data) { - var body = []; - var tr = {}; - var entry = []; - var actions = ''; - - $(data['flow']['actions']).each(function(index, value) { - var locEqualTo = value.indexOf("="); - if ( locEqualTo == -1 ) { - actions += value + ', '; - } else { - var action = value.substr(0,locEqualTo); - if( action == "OUTPUT") { - var portIds = value.substr(locEqualTo+1).split(","); - actions += action + "="; - var allPorts = one.f.flows.registry.nodeports[one.f.flows.registry.selectedNode]['ports']; - for(var i =0; i < portIds.length ; i++) { - var portName = allPorts[portIds[i]]; - actions += portName + ", "; - } - } else { - actions += value + ', '; - } - } - }); - actions = actions.slice(0,-2); - entry.push(actions); + // details + if (details == undefined) { + var $none = $(document.createElement('div')); + $none.addClass('none'); + var $p = $(document.createElement('p')); + $p.text('Please select a flow'); + $p.addClass('text-center').addClass('text-info'); - tr.entry = entry; - body.push(tr); - return body; + $dashlet.append($none) + .append($p); + } + }, + data : { + dashlet : function(data) { + var body = []; + var tr = {}; + var entry = []; + + entry.push(data['name']); + entry.push(data['node']); + entry.push(data['flow']['priority']); + entry.push(data['flow']['hardTimeout']); + entry.push(data['flow']['idleTimeout']); + + tr.entry = entry; + body.push(tr); + return body; + }, + description : function(data) { + var body = []; + var tr = {}; + var entry = []; + entry.push(data['flow']['ingressPort']); + entry.push(data['flow']['etherType']); + entry.push(data['flow']['vlanId']); + entry.push(data['flow']['vlanPriority']); + entry.push(data['flow']['srcMac']); + entry.push(data['flow']['dstMac']); + entry.push(data['flow']['srcIp']); + entry.push(data['flow']['dstIp']); + entry.push(data['flow']['tosBits']); + entry.push(data['flow']['srcPort']); + entry.push(data['flow']['dstPort']); + entry.push(data['flow']['protocol']); + entry.push(data['flow']['cookie']); + + tr.entry = entry; + body.push(tr); + return body; + }, + actions : function(data) { + var body = []; + var tr = {}; + var entry = []; + var actions = ''; + + $(data['flow']['actions']).each(function(index, value) { + var locEqualTo = value.indexOf("="); + if ( locEqualTo == -1 ) { + actions += value + ', '; + } else { + var action = value.substr(0,locEqualTo); + if( action == "OUTPUT") { + var portIds = value.substr(locEqualTo+1).split(","); + actions += action + "="; + var allPorts = one.f.flows.registry.nodeports[one.f.flows.registry.selectedNode]['ports']; + for(var i =0; i < portIds.length ; i++) { + var portName = allPorts[portIds[i]]; + actions += portName + ", "; + } + } else { + actions += value + ', '; + } } + }); + actions = actions.slice(0,-2); + entry.push(actions); + + tr.entry = entry; + body.push(tr); + return body; + } + }, + body : { + dashlet : function(body) { + // create table + var header = ['Flow Name', 'Node', 'Priority', 'Hard Timeout', 'Idle Timeout']; + var $thead = one.lib.dashlet.table.header(header); + var attributes = ['table-striped', 'table-bordered', 'table-condensed']; + var $table = one.lib.dashlet.table.table(attributes); + $table.append($thead); + + var $tbody = one.lib.dashlet.table.body(body); + $table.append($tbody); + + return $table; }, - body : { - dashlet : function(body) { - // create table - var header = ['Flow Name', 'Node', 'Priority', 'Hard Timeout', 'Idle Timeout']; - var $thead = one.lib.dashlet.table.header(header); - var attributes = ['table-striped', 'table-bordered', 'table-condensed']; - var $table = one.lib.dashlet.table.table(attributes); - $table.append($thead); - - var $tbody = one.lib.dashlet.table.body(body); - $table.append($tbody); - - return $table; - }, - description : function(body) { - var header = ['Input Port', 'Ethernet Type', 'VLAN ID', 'VLAN Priority', 'Source MAC', 'Dest MAC', 'Source IP', 'Dest IP', 'ToS', 'Source Port', 'Dest Port', 'Protocol', 'Cookie']; - var $thead = one.lib.dashlet.table.header(header); - var attributes = ['table-striped', 'table-bordered', 'table-condensed']; - var $table = one.lib.dashlet.table.table(attributes); - $table.append($thead); + description : function(body) { + var header = ['Input Port', 'Ethernet Type', 'VLAN ID', 'VLAN Priority', 'Source MAC', 'Dest MAC', 'Source IP', 'Dest IP', 'ToS', 'Source Port', 'Dest Port', 'Protocol', 'Cookie']; + var $thead = one.lib.dashlet.table.header(header); + var attributes = ['table-striped', 'table-bordered', 'table-condensed']; + var $table = one.lib.dashlet.table.table(attributes); + $table.append($thead); - var $tbody = one.lib.dashlet.table.body(body); - $table.append($tbody); + var $tbody = one.lib.dashlet.table.body(body); + $table.append($tbody); - return $table; - }, - actions : function(body) { - var header = ['Actions']; - var $thead = one.lib.dashlet.table.header(header); - var attributes = ['table-striped', 'table-bordered', 'table-condensed']; - var $table = one.lib.dashlet.table.table(attributes); - $table.append($thead); + return $table; + }, + actions : function(body) { + var header = ['Actions']; + var $thead = one.lib.dashlet.table.header(header); + var attributes = ['table-striped', 'table-bordered', 'table-condensed']; + var $table = one.lib.dashlet.table.table(attributes); + $table.append($thead); - var $tbody = one.lib.dashlet.table.body(body); - $table.append($tbody); + var $tbody = one.lib.dashlet.table.body(body); + $table.append($tbody); - return $table; - } + return $table; } + } } /** FLOW ENTRIES **/ one.f.flows = { - id : { - dashlet : { - add : "one_f_flows_id_dashlet_add", - removeMultiple : "one_f_flows_id_dashlet_removeMultiple", - remove : "one_f_flows_id_dashlet_remove", - toggle : "one_f_flows_id_dashlet_toggle", - edit : "one_f_flows_id_dashlet_edit", - datagrid : "one_f_flows_id_dashlet_datagrid", - selectAllFlows : "one_f_flows_id_dashlet_selectAllFlows" - }, + id : { + dashlet : { + add : "one_f_flows_id_dashlet_add", + removeMultiple : "one_f_flows_id_dashlet_removeMultiple", + remove : "one_f_flows_id_dashlet_remove", + toggle : "one_f_flows_id_dashlet_toggle", + edit : "one_f_flows_id_dashlet_edit", + datagrid : "one_f_flows_id_dashlet_datagrid", + selectAllFlows : "one_f_flows_id_dashlet_selectAllFlows" + }, + modal : { + install : "one_f_flows_id_modal_install", + edit : "one_f_flows_id_modal_edit", + add : "one_f_flows_id_modal_add", + close : "one_f_flows_id_modal_close", + modal : "one_f_flows_id_modal_modal", + dialog : { + modal : "one_f_flows_id_modal_dialog_modal", + remove : "one_f_flows_id_modal_dialog_remove", + close : "one_f_flows_id_modal_dialog_close" + }, + action : { + button : "one_f_flows_id_modal_action_button", + modal : "one_f_flows_id_modal_action_modal", + add : "one_f_flows_id_modal_action_add", + close : "one_f_flows_id_modal_action_close", + table : "one_f_flows_id_modal_action_table", + addOutputPorts : "one_f_flows_id_modal_action_addOutputPorts", + setVlanId : "one_f_flows_id_modal_action_setVlanId", + setVlanPriority : "one_f_flows_id_modal_action_setVlanPriority", + modifyDatalayerSourceAddress : "one_f_flows_id_modal_action_modifyDatalayerSourceAddress", + modifyDatalayerDestinationAddress : "one_f_flows_id_modal_action_modifyDatalayerDestinationAddress", + modifyNetworkSourceAddress : "one_f_flows_modal_action_modifyNetworkSourceAddress", + modifyNetworkDestinationAddress : "one_f_flows_modal_action_modifyNetworkDestinationAddress", + modifyTosBits : "one_f_flows_modal_action_modifyTosBits", + modifyTransportSourcePort : "one_f_flows_modal_action_modifyTransportSourcePort", + modifyTransportDestinationPort : "one_f_flows_modal_action_modifyTransportDestinationPort", + enqueue : 'one-f-flows-modal-action-enqueue', + queue : 'one-f-flows-modal-action-queue', + setEthertype : 'one-f-flows-modal-action-setEthertype', + pushVlan : 'one-f-flows-modal-action-pushVlan', + setVlanCfi : 'one-f-flows-modal-action-setVlanCfi', modal : { - install : "one_f_flows_id_modal_install", - edit : "one_f_flows_id_modal_edit", - add : "one_f_flows_id_modal_add", - close : "one_f_flows_id_modal_close", - modal : "one_f_flows_id_modal_modal", - dialog : { - modal : "one_f_flows_id_modal_dialog_modal", - remove : "one_f_flows_id_modal_dialog_remove", - close : "one_f_flows_id_modal_dialog_close" - }, - action : { - button : "one_f_flows_id_modal_action_button", - modal : "one_f_flows_id_modal_action_modal", - add : "one_f_flows_id_modal_action_add", - close : "one_f_flows_id_modal_action_close", - table : "one_f_flows_id_modal_action_table", - addOutputPorts : "one_f_flows_id_modal_action_addOutputPorts", - setVlanId : "one_f_flows_id_modal_action_setVlanId", - setVlanPriority : "one_f_flows_id_modal_action_setVlanPriority", - modifyDatalayerSourceAddress : "one_f_flows_id_modal_action_modifyDatalayerSourceAddress", - modifyDatalayerDestinationAddress : "one_f_flows_id_modal_action_modifyDatalayerDestinationAddress", - modifyNetworkSourceAddress : "one_f_flows_modal_action_modifyNetworkSourceAddress", - modifyNetworkDestinationAddress : "one_f_flows_modal_action_modifyNetworkDestinationAddress", - modifyTosBits : "one_f_flows_modal_action_modifyTosBits", - modifyTransportSourcePort : "one_f_flows_modal_action_modifyTransportSourcePort", - modifyTransportDestinationPort : "one_f_flows_modal_action_modifyTransportDestinationPort", - modal : { - modal : "one_f_flows_modal_action_modal_modal", - remove : "one_f_flows_modal_action_modal_remove", - cancel : "one_f_flows_modal_action_modal_cancel" - } - }, - form : { - name : "one_f_flows_id_modal_form_name", - nodes : "one_f_flows_id_modal_form_nodes", - port : "one_f_flows_id_modal_form_port", - priority : "one_f_flows_id_modal_form_priority", - hardTimeout : "one_f_flows_id_modal_form_hardTimeout", - idleTimeout : "one_f_flows_id_modal_form_idleTimeout", - cookie : "one_f_flows_id_modal_form_cookie", - etherType : "one_f_flows_id_modal_form_etherType", - vlanId : "one_f_flows_id_modal_form_vlanId", - vlanPriority : "one_f_flows_id_modal_form_vlanPriority", - srcMac : "one_f_flows_id_modal_form_srcMac", - dstMac : "one_f_flows_id_modal_form_dstMac", - srcIp : "one_f_flows_id_modal_form_srcIp", - dstIp : "one_f_flows_id_modal_form_dstIp", - tosBits : "one_f_flows_id_modal_form_tosBits", - srcPort : "one_f_flows_id_modal_form_srcPort", - dstPort : "one_f_flows_id_modal_form_dstPort", - protocol : "one_f_flows_id_modal_form_protocol" - } + modal : "one_f_flows_modal_action_modal_modal", + remove : "one_f_flows_modal_action_modal_remove", + cancel : "one_f_flows_modal_action_modal_cancel" } - }, - registry : {}, - dashlet : function($dashlet, callback) { - - // load body - one.f.flows.ajax.dashlet(function(data) { - - var $h4 = one.lib.dashlet.header("Flow Entries"); - - $dashlet.append($h4); - if (one.f.flows.registry.privilege === 'WRITE') { - var button = one.lib.dashlet.button.single("Add Flow Entry", one.f.flows.id.dashlet.add, "btn-primary", "btn-mini"); - var $button = one.lib.dashlet.button.button(button); - - $button.click(function() { - var $modal = one.f.flows.modal.initialize(); - $modal.modal(); - }); - $dashlet.append($button); - var button = one.lib.dashlet.button.single("Remove Flow Entry", one.f.flows.id.dashlet.removeMultiple, "btn-danger", "btn-mini"); - var $button = one.lib.dashlet.button.button(button); - - $button.click(function() { - var checkedCheckBoxes = $('.flowEntry[type=checkbox]:checked'); - if (checkedCheckBoxes.size() === 0) { - return false; - } - - var requestData = []; - - checkedCheckBoxes.each(function(index, value) { - var flowEntry = {}; - flowEntry['name'] = checkedCheckBoxes[index].name; - flowEntry['node'] = checkedCheckBoxes[index].getAttribute("node"); - requestData.push(flowEntry); - }); - one.f.flows.modal.removeMultiple.dialog(requestData); - }); - $dashlet.append($button); + }, + form : { + name : "one_f_flows_id_modal_form_name", + nodes : "one_f_flows_id_modal_form_nodes", + port : "one_f_flows_id_modal_form_port", + priority : "one_f_flows_id_modal_form_priority", + hardTimeout : "one_f_flows_id_modal_form_hardTimeout", + idleTimeout : "one_f_flows_id_modal_form_idleTimeout", + cookie : "one_f_flows_id_modal_form_cookie", + etherType : "one_f_flows_id_modal_form_etherType", + vlanId : "one_f_flows_id_modal_form_vlanId", + vlanPriority : "one_f_flows_id_modal_form_vlanPriority", + srcMac : "one_f_flows_id_modal_form_srcMac", + dstMac : "one_f_flows_id_modal_form_dstMac", + srcIp : "one_f_flows_id_modal_form_srcIp", + dstIp : "one_f_flows_id_modal_form_dstIp", + tosBits : "one_f_flows_id_modal_form_tosBits", + srcPort : "one_f_flows_id_modal_form_srcPort", + dstPort : "one_f_flows_id_modal_form_dstPort", + protocol : "one_f_flows_id_modal_form_protocol", + action : 'one-f-flows-id-modal-form-action' + } + } + }, + registry : {}, + dashlet : function($dashlet, callback) { + // load body + one.f.flows.ajax.dashlet(function(data) { + var $h4 = one.lib.dashlet.header("Flow Entries"); + $dashlet.append($h4); + if (one.f.flows.registry.privilege === 'WRITE') { + var button = one.lib.dashlet.button.single("Add Flow Entry", one.f.flows.id.dashlet.add, "btn-primary", "btn-mini"); + var $button = one.lib.dashlet.button.button(button); + + $button.click(function() { + var $modal = one.f.flows.modal.initialize(); + $modal.modal(); + }); + $dashlet.append($button); + var button = one.lib.dashlet.button.single("Remove Flow Entry", one.f.flows.id.dashlet.removeMultiple, "btn-danger", "btn-mini"); + var $button = one.lib.dashlet.button.button(button); + + $button.click(function() { + var checkedCheckBoxes = $('.flowEntry[type=checkbox]:checked'); + if (checkedCheckBoxes.size() === 0) { + return false; + } - } + var requestData = []; - var $gridHTML = one.lib.dashlet.datagrid.init(one.f.flows.id.dashlet.datagrid, { - searchable: true, - filterable: false, - pagination: true, - flexibleRowsPerPage: true - }, "table-striped table-condensed"); - $dashlet.append($gridHTML); - var dataSource = one.f.flows.data.flowsDataGrid(data); - $("#" + one.f.flows.id.dashlet.datagrid).datagrid({dataSource: dataSource}).on("loaded", function() { - $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows).click(function() { - $("#" + one.f.flows.id.dashlet.datagrid).find(':checkbox').prop('checked', - $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows).is(':checked')); - }); - - $("#" + one.f.flows.id.dashlet.datagrid).find("tbody tr").each(function(index, tr) { - $tr = $(tr); - $span = $("td span", $tr); - var flowstatus = $span.data("flowstatus"); - if($span.data("installinhw") != null) { - var installInHw = $span.data("installinhw").toString(); - if(installInHw == "true" && flowstatus == "Success") { - $tr.addClass("success"); - } else { - $tr.addClass("warning"); - } - } - // attach mouseover to show pointer cursor - $tr.mouseover(function() { - $(this).css("cursor", "pointer"); - }); - // attach click event - $tr.click(function() { - var $td = $($(this).find("td")[1]); - var id = $td.text(); - var node = $td.find("span").data("nodeid"); - one.f.flows.detail(id, node); - }); - $(".flowEntry").click(function(e){ - if (!$('.flowEntry[type=checkbox]:not(:checked)').length) { - $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows) - .prop("checked", - true); - } else { - $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows) - .prop("checked", - false); - } - e.stopPropagation(); - }); - }); - }); - - // details callback - if(callback != undefined) callback(); + checkedCheckBoxes.each(function(index, value) { + var flowEntry = {}; + flowEntry['name'] = checkedCheckBoxes[index].name; + flowEntry['node'] = checkedCheckBoxes[index].getAttribute("node"); + requestData.push(flowEntry); + }); + one.f.flows.modal.removeMultiple.dialog(requestData); }); - }, - detail : function(id, node) { - // clear flow details - var $detailDashlet = one.main.dashlet.right.bottom; - $detailDashlet.empty(); - var $h4 = one.lib.dashlet.header("Flow Overview"); - $detailDashlet.append($h4); - - // details - var flows = one.f.flows.registry.flows; - one.f.flows.registry['selectedId'] = id; - one.f.flows.registry['selectedNode'] = node; - var flow; - $(flows).each(function(index, value) { - if (value.name == id && value.nodeId == node) { - flow = value; - } + $dashlet.append($button); + + } + var $gridHTML = one.lib.dashlet.datagrid.init(one.f.flows.id.dashlet.datagrid, { + searchable: true, + filterable: false, + pagination: true, + flexibleRowsPerPage: true + }, "table-striped table-condensed"); + $dashlet.append($gridHTML); + var dataSource = one.f.flows.data.flowsDataGrid(data); + $("#" + one.f.flows.id.dashlet.datagrid).datagrid({dataSource: dataSource}).on("loaded", function() { + $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows).click(function() { + $("#" + one.f.flows.id.dashlet.datagrid).find(':checkbox').prop('checked', + $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows).is(':checked')); }); - if (one.f.flows.registry.privilege === 'WRITE') { - // remove button - var button = one.lib.dashlet.button.single("Remove Flow", one.f.flows.id.dashlet.remove, "btn-danger", "btn-mini"); - var $button = one.lib.dashlet.button.button(button); - $button.click(function() { - var $modal = one.f.flows.modal.dialog.initialize(id, node); - $modal.modal(); - }); - // edit button - var editButton = one.lib.dashlet.button.single("Edit Flow", one.f.flows.id.dashlet.edit, "btn-primary", "btn-mini"); - var $editButton = one.lib.dashlet.button.button(editButton); - $editButton.click(function() { - var install = flow['flow']['installInHw']; - var $modal = one.f.flows.modal.initialize(true,install); - $modal.modal().on('shown',function(){ - var $port = $('#'+one.f.flows.id.modal.form.port); - $('#'+one.f.flows.id.modal.form.nodes).trigger("change"); - }); - }); - // toggle button - var toggle; - if (flow['flow']['installInHw'] == 'true' && flow['flow']['status'] == 'Success') { - toggle = one.lib.dashlet.button.single("Uninstall Flow", one.f.flows.id.dashlet.toggle, "btn-warning", "btn-mini"); - } else { - toggle = one.lib.dashlet.button.single("Install Flow", one.f.flows.id.dashlet.toggle, "btn-success", "btn-mini"); - } - var $toggle = one.lib.dashlet.button.button(toggle); - $toggle.click(function() { - one.f.flows.modal.ajax.toggleflow(id, node, function(data) { - if(data == "Success") { - one.main.dashlet.right.bottom.empty(); - one.f.detail.dashlet(one.main.dashlet.right.bottom); - one.main.dashlet.left.top.empty(); - one.f.flows.dashlet(one.main.dashlet.left.top, function() { - // checks are backwards due to stale registry - if(flow['flow']['installInHw'] == 'true') { - one.lib.alert('Uninstalled Flow'); - } else { - one.lib.alert('Installed Flow'); - } - one.f.flows.detail(id, node) - }); - } else { - one.lib.alert('Cannot toggle flow: '+data); - } - }); - }); - $detailDashlet.append($button).append($editButton).append($toggle); - } - // append details - var body = one.f.detail.data.dashlet(flow); - var $body = one.f.detail.body.dashlet(body); - $detailDashlet.append($body); - var body = one.f.detail.data.description(flow); - var $body = one.f.detail.body.description(body); - $detailDashlet.append($body); - var body = one.f.detail.data.actions(flow); - var $body = one.f.detail.body.actions(body); - $detailDashlet.append($body); - }, - modal : { - dialog : { - initialize : function(id, node) { - var h3 = "Remove Flow"; - var $p = one.f.flows.modal.dialog.body(id); - var footer = one.f.flows.modal.dialog.footer(); - var $modal = one.lib.modal.spawn(one.f.flows.id.modal.dialog.modal, h3, $p, footer); - $('#'+one.f.flows.id.modal.dialog.close, $modal).click(function() { - $modal.modal('hide'); - }); - $('#'+one.f.flows.id.modal.dialog.remove, $modal).click(function() { - one.f.flows.modal.ajax.removeflow(id, node, function(data) { - if (data == "Success") { - $modal.modal('hide'); - one.main.dashlet.right.bottom.empty(); - one.f.detail.dashlet(one.main.dashlet.right.bottom); - one.main.dashlet.left.top.empty(); - one.f.flows.dashlet(one.main.dashlet.left.top); - one.lib.alert('Flow removed'); - } else { - one.lib.alert('Cannot remove flow: '+data); - } - }); - }); - return $modal; - }, - footer : function() { - var footer = []; - - var removeButton = one.lib.dashlet.button.single("Remove Flow", one.f.flows.id.modal.dialog.remove, "btn-danger", ""); - var $removeButton = one.lib.dashlet.button.button(removeButton); - footer.push($removeButton); - - var closeButton = one.lib.dashlet.button.single("Cancel", one.f.flows.id.modal.dialog.close, "", ""); - var $closeButton = one.lib.dashlet.button.button(closeButton); - footer.push($closeButton); - - return footer; - }, - body : function(id) { - var $p = $(document.createElement('p')); - $p.append('Remove flow '+id+'?'); - return $p; + $("#" + one.f.flows.id.dashlet.datagrid).find("tbody tr").each(function(index, tr) { + $tr = $(tr); + $span = $("td span", $tr); + var flowstatus = $span.data("flowstatus"); + if($span.data("installinhw") != null) { + var installInHw = $span.data("installinhw").toString(); + if(installInHw == "true" && flowstatus == "Success") { + $tr.addClass("success"); + } else { + $tr.addClass("warning"); } - }, - initialize : function(edit,install) { - var h3; - if(edit) { - h3 = "Edit Flow Entry"; - var footer = one.f.flows.modal.footerEdit(); - + } + // attach mouseover to show pointer cursor + $tr.mouseover(function() { + $(this).css("cursor", "pointer"); + }); + // attach click event + $tr.click(function() { + var $td = $($(this).find("td")[1]); + var id = $td.text(); + var node = $td.find("span").data("nodeid"); + one.f.flows.detail(id, node); + }); + $(".flowEntry").click(function(e){ + if (!$('.flowEntry[type=checkbox]:not(:checked)').length) { + $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows) + .prop("checked", + true); } else { - h3 = "Add Flow Entry"; - var footer = one.f.flows.modal.footer(); + $("#"+one.f.flows.id.dashlet.datagrid.selectAllFlows) + .prop("checked", + false); } - - var $modal = one.lib.modal.spawn(one.f.flows.id.modal.modal, h3, "", footer); - - // bind close button - $('#'+one.f.flows.id.modal.close, $modal).click(function() { - $modal.modal('hide'); + e.stopPropagation(); + }); + }); + }); + + // details callback + if(callback != undefined) callback(); + }); + }, + detail : function(id, node) { + // clear flow details + var $detailDashlet = one.main.dashlet.right.bottom; + $detailDashlet.empty(); + var $h4 = one.lib.dashlet.header("Flow Overview"); + $detailDashlet.append($h4); + + // details + var flows = one.f.flows.registry.flows; + one.f.flows.registry['selectedId'] = id; + one.f.flows.registry['selectedNode'] = node; + var flow; + $(flows).each(function(index, value) { + if (value.name == id && value.nodeId == node) { + flow = value; + } + }); + if (one.f.flows.registry.privilege === 'WRITE') { + // remove button + var button = one.lib.dashlet.button.single("Remove Flow", one.f.flows.id.dashlet.remove, "btn-danger", "btn-mini"); + var $button = one.lib.dashlet.button.button(button); + $button.click(function() { + var $modal = one.f.flows.modal.dialog.initialize(id, node); + $modal.modal(); + }); + // edit button + var editButton = one.lib.dashlet.button.single("Edit Flow", one.f.flows.id.dashlet.edit, "btn-primary", "btn-mini"); + var $editButton = one.lib.dashlet.button.button(editButton); + $editButton.click(function() { + var install = flow['flow']['installInHw']; + var $modal = one.f.flows.modal.initialize(true,install); + $modal.modal().on('shown',function(){ + var $port = $('#'+one.f.flows.id.modal.form.port); + $('#'+one.f.flows.id.modal.form.nodes).trigger("change"); + }); + }); + // toggle button + var toggle; + if (flow['flow']['installInHw'] == 'true' && flow['flow']['status'] == 'Success') { + toggle = one.lib.dashlet.button.single("Uninstall Flow", one.f.flows.id.dashlet.toggle, "btn-warning", "btn-mini"); + } else { + toggle = one.lib.dashlet.button.single("Install Flow", one.f.flows.id.dashlet.toggle, "btn-success", "btn-mini"); + } + var $toggle = one.lib.dashlet.button.button(toggle); + $toggle.click(function() { + one.f.flows.modal.ajax.toggleflow(id, node, function(data) { + if(data == "Success") { + one.main.dashlet.right.bottom.empty(); + one.f.detail.dashlet(one.main.dashlet.right.bottom); + one.main.dashlet.left.top.empty(); + one.f.flows.dashlet(one.main.dashlet.left.top, function() { + // checks are backwards due to stale registry + if(flow['flow']['installInHw'] == 'true') { + one.lib.alert('Uninstalled Flow'); + } else { + one.lib.alert('Installed Flow'); + } + one.f.flows.detail(id, node) }); + } else { + one.lib.alert('Cannot toggle flow: '+data); + } + }); + }); - if (edit) { - // bind edit flow button - $('#'+one.f.flows.id.modal.edit, $modal).click(function() { - one.f.flows.modal.save($modal, install, true); - }); + $detailDashlet.append($button).append($editButton).append($toggle); + } + // append details + var body = one.f.detail.data.dashlet(flow); + var $body = one.f.detail.body.dashlet(body); + $detailDashlet.append($body); + var body = one.f.detail.data.description(flow); + var $body = one.f.detail.body.description(body); + $detailDashlet.append($body); + var body = one.f.detail.data.actions(flow); + var $body = one.f.detail.body.actions(body); + $detailDashlet.append($body); + }, + modal : { + dialog : { + initialize : function(id, node) { + var h3 = "Remove Flow"; + var $p = one.f.flows.modal.dialog.body(id); + var footer = one.f.flows.modal.dialog.footer(); + var $modal = one.lib.modal.spawn(one.f.flows.id.modal.dialog.modal, h3, $p, footer); + $('#'+one.f.flows.id.modal.dialog.close, $modal).click(function() { + $modal.modal('hide'); + }); + $('#'+one.f.flows.id.modal.dialog.remove, $modal).click(function() { + one.f.flows.modal.ajax.removeflow(id, node, function(data) { + if (data == "Success") { + $modal.modal('hide'); + one.main.dashlet.right.bottom.empty(); + one.f.detail.dashlet(one.main.dashlet.right.bottom); + one.main.dashlet.left.top.empty(); + one.f.flows.dashlet(one.main.dashlet.left.top); + one.lib.alert('Flow removed'); } else { - // bind add flow button - $('#'+one.f.flows.id.modal.add, $modal).click(function() { - one.f.flows.modal.save($modal, 'false'); - }); - - // bind install flow button - $('#'+one.f.flows.id.modal.install, $modal).click(function() { - one.f.flows.modal.save($modal, 'true'); - }); + one.lib.alert('Cannot remove flow: '+data); } + }); + }); + return $modal; + }, + footer : function() { + var footer = []; + + var removeButton = one.lib.dashlet.button.single("Remove Flow", one.f.flows.id.modal.dialog.remove, "btn-danger", ""); + var $removeButton = one.lib.dashlet.button.button(removeButton); + footer.push($removeButton); + + var closeButton = one.lib.dashlet.button.single("Cancel", one.f.flows.id.modal.dialog.close, "", ""); + var $closeButton = one.lib.dashlet.button.button(closeButton); + footer.push($closeButton); + + return footer; + }, + body : function(id) { + var $p = $(document.createElement('p')); + $p.append('Remove flow '+id+'?'); + return $p; + } + }, + initialize : function(edit,install) { + var h3; + if(edit) { + h3 = "Edit Flow Entry"; + var footer = one.f.flows.modal.footerEdit(); + + } else { + h3 = "Add Flow Entry"; + var footer = one.f.flows.modal.footer(); + } + + var $modal = one.lib.modal.spawn(one.f.flows.id.modal.modal, h3, "", footer); + + // bind close button + $('#'+one.f.flows.id.modal.close, $modal).click(function() { + $modal.modal('hide'); + }); + + if (edit) { + // bind edit flow button + $('#'+one.f.flows.id.modal.edit, $modal).click(function() { + one.f.flows.modal.save($modal, install, true); + }); + } else { + // bind add flow button + $('#'+one.f.flows.id.modal.add, $modal).click(function() { + one.f.flows.modal.save($modal, 'false'); + }); + // bind install flow button + $('#'+one.f.flows.id.modal.install, $modal).click(function() { + one.f.flows.modal.save($modal, 'true'); + }); + } - var nodes = one.f.flows.registry.nodes; - var nodeports = one.f.flows.registry.nodeports; - var $body = one.f.flows.modal.body(nodes, nodeports, edit); - one.lib.modal.inject.body($modal, $body,edit); - - return $modal; - }, - save : function($modal, install, edit) { - var result = {}; - - result['name'] = $('#'+one.f.flows.id.modal.form.name, $modal).val(); - result['ingressPort'] = $('#'+one.f.flows.id.modal.form.port, $modal).val(); - result['priority'] = $('#'+one.f.flows.id.modal.form.priority, $modal).val(); - result['hardTimeout'] = $('#'+one.f.flows.id.modal.form.hardTimeout, $modal).val(); - result['idleTimeout'] = $('#'+one.f.flows.id.modal.form.idleTimeout, $modal).val(); - result['cookie'] = $('#'+one.f.flows.id.modal.form.cookie, $modal).val(); - result['etherType'] = $('#'+one.f.flows.id.modal.form.etherType, $modal).val(); - result['vlanId'] = $('#'+one.f.flows.id.modal.form.vlanId, $modal).val(); - result['vlanPriority'] = $('#'+one.f.flows.id.modal.form.vlanPriority, $modal).val(); - result['dlSrc'] = $('#'+one.f.flows.id.modal.form.srcMac, $modal).val(); - result['dlDst'] = $('#'+one.f.flows.id.modal.form.dstMac, $modal).val(); - result['nwSrc'] = $('#'+one.f.flows.id.modal.form.srcIp, $modal).val(); - result['nwDst'] = $('#'+one.f.flows.id.modal.form.dstIp, $modal).val(); - result['tosBits'] = $('#'+one.f.flows.id.modal.form.tosBits, $modal).val(); - result['tpSrc'] = $('#'+one.f.flows.id.modal.form.srcPort, $modal).val(); - result['tpDst'] = $('#'+one.f.flows.id.modal.form.dstPort, $modal).val(); - result['protocol'] = $('#'+one.f.flows.id.modal.form.protocol, $modal).val(); - result['installInHw'] = install; - - var nodeId = $('#'+one.f.flows.id.modal.form.nodes, $modal).val(); - - $.each(result, function(key, value) { - if (value == "") delete result[key]; - }); + var nodes = one.f.flows.registry.nodes; + var nodeports = one.f.flows.registry.nodeports; + var $body = one.f.flows.modal.body(nodes, nodeports, edit); + one.lib.modal.inject.body($modal, $body,edit); - var action = []; - var $table = $('#'+one.f.flows.id.modal.action.table, $modal); - $($table.find('tbody').find('tr')).each(function(index, value) { - if (!$(this).find('td').hasClass('empty')) { - action.push($(value).data('action')); - } + return $modal; + }, + save : function($modal, install, edit) { + var result = {}; + + result['name'] = $('#'+one.f.flows.id.modal.form.name, $modal).val(); + result['ingressPort'] = $('#'+one.f.flows.id.modal.form.port, $modal).val(); + result['priority'] = $('#'+one.f.flows.id.modal.form.priority, $modal).val(); + result['hardTimeout'] = $('#'+one.f.flows.id.modal.form.hardTimeout, $modal).val(); + result['idleTimeout'] = $('#'+one.f.flows.id.modal.form.idleTimeout, $modal).val(); + result['cookie'] = $('#'+one.f.flows.id.modal.form.cookie, $modal).val(); + result['etherType'] = $('#'+one.f.flows.id.modal.form.etherType, $modal).val(); + result['vlanId'] = $('#'+one.f.flows.id.modal.form.vlanId, $modal).val(); + result['vlanPriority'] = $('#'+one.f.flows.id.modal.form.vlanPriority, $modal).val(); + result['dlSrc'] = $('#'+one.f.flows.id.modal.form.srcMac, $modal).val(); + result['dlDst'] = $('#'+one.f.flows.id.modal.form.dstMac, $modal).val(); + result['nwSrc'] = $('#'+one.f.flows.id.modal.form.srcIp, $modal).val(); + result['nwDst'] = $('#'+one.f.flows.id.modal.form.dstIp, $modal).val(); + result['tosBits'] = $('#'+one.f.flows.id.modal.form.tosBits, $modal).val(); + result['tpSrc'] = $('#'+one.f.flows.id.modal.form.srcPort, $modal).val(); + result['tpDst'] = $('#'+one.f.flows.id.modal.form.dstPort, $modal).val(); + result['protocol'] = $('#'+one.f.flows.id.modal.form.protocol, $modal).val(); + result['installInHw'] = install; + + var nodeId = $('#'+one.f.flows.id.modal.form.nodes, $modal).val(); + + $.each(result, function(key, value) { + if (value == "") delete result[key]; + }); + + var action = []; + var $table = $('#'+one.f.flows.id.modal.action.table, $modal); + $($table.find('tbody').find('tr')).each(function(index, value) { + if (!$(this).find('td').hasClass('empty')) { + action.push($(value).data('action')); + } + }); + result['actions'] = action; + + // frontend validation + if (result['name'] == undefined) { + alert('Need flow name'); + return; + } + if (nodeId == '') { + alert('Select node'); + return; + } + if (action.length == 0) { + alert('Please specify an action'); + return; + } + + // package for ajax call + var resource = {}; + resource['body'] = JSON.stringify(result); + if(edit){ + resource['action'] = 'edit'; + } else { + resource['action'] = 'add'; + } + + resource['nodeId'] = nodeId; + + if (edit) { + one.f.flows.modal.ajax.saveflow(resource, function(data) { + if (data == "Success") { + $modal.modal('hide').on('hidden', function () { + one.f.flows.detail(result['name'], nodeId); }); - result['actions'] = action; - - // frontend validation - if (result['name'] == undefined) { - alert('Need flow name'); - return; - } - if (nodeId == '') { - alert('Select node'); - return; - } - if (action.length == 0) { - alert('Please specify an action'); - return; - } - - // package for ajax call - var resource = {}; - resource['body'] = JSON.stringify(result); - if(edit){ - resource['action'] = 'edit'; + one.lib.alert('Flow Entry edited'); + one.main.dashlet.left.top.empty(); + one.f.flows.dashlet(one.main.dashlet.left.top); + } else { + alert('Could not edit flow: '+data); + } + }); + } else { + one.f.flows.modal.ajax.saveflow(resource, function(data) { + if (data == "Success") { + $modal.modal('hide'); + one.lib.alert('Flow Entry added'); + one.main.dashlet.left.top.empty(); + one.f.flows.dashlet(one.main.dashlet.left.top); + } else { + alert('Could not add flow: '+data); + } + }); + } + }, + ajax : { + nodes : function(successCallback) { + $.getJSON(one.f.address.root+one.f.address.flows.nodes, function(data) { + var nodes = one.f.flows.modal.data.nodes(data); + var nodeports = data; + one.f.flows.registry['nodes'] = nodes; + one.f.flows.registry['nodeports'] = nodeports; + + successCallback(nodes, nodeports); + }); + }, + saveflow : function(resource, callback) { + $.post(one.f.address.root+one.f.address.flows.flow, resource, function(data) { + callback(data); + }); + }, + removeflow : function(id, node, callback) { + resource = {}; + resource['action'] = 'remove'; + $.post(one.f.address.root+one.f.address.flows.flow+'/'+node+'/'+id, resource, function(data) { + callback(data); + }); + }, + toggleflow : function(id, node, callback) { + resource = {}; + resource['action'] = 'toggle'; + $.post(one.f.address.root+one.f.address.flows.flow+'/'+node+'/'+id, resource, function(data) { + callback(data); + }); + } + }, + data : { + nodes : function(data) { + result = {}; + $.each(data, function(key, value) { + result[key] = value['name']; + }); + return result; + } + }, + body : function(nodes, nodeports, edit) { + var $form = $(document.createElement('form')); + var $fieldset = $(document.createElement('fieldset')); + var existingFlow; + // flow description + var $legend = one.lib.form.legend(""); + $legend.css('visibility', 'hidden'); + $fieldset.append($legend); + // name + var $label = one.lib.form.label("Name"); + var $input = one.lib.form.input("Flow Name"); + $input.attr('id', one.f.flows.id.modal.form.name); + if(edit) { + $input.attr('disabled', 'disabled'); + var flows = one.f.flows.registry.flows; + $(flows).each(function(index, value) { + if (value.name == one.f.flows.registry.selectedId && value.nodeId == one.f.flows.registry.selectedNode) { + existingFlow = value.flow; + } + }); + $input.val(existingFlow.name); + } + + $fieldset.append($label).append($input); + // node + var $label = one.lib.form.label("Node"); + var $select = one.lib.form.select.create(nodes); + one.lib.form.select.prepend($select, { '' : 'Please Select a Node' }); + $select.val($select.find("option:first").val()); + $select.attr('id', one.f.flows.id.modal.form.nodes); + if(edit) { + $select.attr('disabled', 'disabled'); + $select.val(existingFlow.node.type + "|"+ existingFlow.node.nodeIDString); + } + + // bind onchange + $select.change(function() { + // retrieve port value + var node = $(this).find('option:selected').attr('value'); + var $ports = $('#'+one.f.flows.id.modal.form.port); + if (node == '') { + one.lib.form.select.inject($ports, {}); + return; + } + one.f.flows.registry['currentNode'] = node; + var ports = nodeports[node]['ports']; + one.lib.form.select.inject($ports, ports); + one.lib.form.select.prepend($ports, { '' : 'Please Select a Port' }); + $ports.val($ports.find("option:first").val()); + if(edit) { + $ports.val( existingFlow.ingressPort ); + } + $.getJSON(one.f.address.root+'/valid-flows/'+node, function(response) { + var $select = $('#'+one.f.flows.id.modal.form.action, $fieldset); + one.lib.form.select.inject($select, response); + one.lib.form.select.prepend($select, {'' : 'Please Select an Action'}); + // when selecting an action + $select.change(function() { + var action = $(this).find('option:selected'); + one.f.flows.modal.action.parse(action.attr('value')); + $select[0].selectedIndex = 0; + }); + }); + }); + + $fieldset.append($label).append($select); + // input port + var $label = one.lib.form.label("Input Port"); + var $select = one.lib.form.select.create(); + + $select.attr('id', one.f.flows.id.modal.form.port); + $fieldset.append($label).append($select); + // priority + var $label = one.lib.form.label("Priority"); + var $input = one.lib.form.input("Priority"); + $input.attr('id', one.f.flows.id.modal.form.priority); + $input.val('500'); + $fieldset.append($label).append($input); + if(edit) { + $input.val(existingFlow.priority); + } + // hardTimeout + var $label = one.lib.form.label("Hard Timeout"); + var $input = one.lib.form.input("Hard Timeout"); + $input.attr('id', one.f.flows.id.modal.form.hardTimeout); + if(edit) { + $input.val(existingFlow.hardTimeout); + } + $fieldset.append($label).append($input); + + // idleTimeout + var $label = one.lib.form.label("Idle Timeout"); + var $input = one.lib.form.input("Idle Timeout"); + $input.attr('id', one.f.flows.id.modal.form.idleTimeout); + $fieldset.append($label).append($input); + if(edit) { + $input.val(existingFlow.idleTimeout); + } + // cookie + var $label = one.lib.form.label("Cookie"); + var $input = one.lib.form.input("Cookie"); + $input.attr('id', one.f.flows.id.modal.form.cookie); + $fieldset.append($label).append($input); + if(edit) { + $input.val(existingFlow.cookie); + } + + // layer 2 + var $legend = one.lib.form.legend("Layer 2"); + $fieldset.append($legend); + // etherType + var $label = one.lib.form.label("Ethernet Type"); + var $input = one.lib.form.input("Ethernet Type"); + $input.attr('id', one.f.flows.id.modal.form.etherType); + $input.val('0x800'); + $fieldset.append($label).append($input); + if(edit) { + $input.val(existingFlow.etherType); + } + // vlanId + var $label = one.lib.form.label("VLAN Identification Number"); + var $input = one.lib.form.input("VLAN Identification Number"); + $input.attr('id', one.f.flows.id.modal.form.vlanId); + var $help = one.lib.form.help("Range: 0 - 4095"); + $fieldset.append($label).append($input).append($help); + if(edit) { + $input.val(existingFlow.vlanId); + } + + // vlanPriority + var $label = one.lib.form.label("VLAN Priority"); + var $input = one.lib.form.input("VLAN Priority"); + $input.attr('id', one.f.flows.id.modal.form.vlanPriority); + var $help = one.lib.form.help("Range: 0 - 7"); + $fieldset.append($label).append($input).append($help); + if(edit) { + $input.val(existingFlow.vlanPriority); + } + + // srcMac + var $label = one.lib.form.label("Source MAC Address"); + var $input = one.lib.form.input("3c:97:0e:75:c3:f7"); + $input.attr('id', one.f.flows.id.modal.form.srcMac); + $fieldset.append($label).append($input); + if(edit) { + $input.val(existingFlow.srcMac); + } + // dstMac + var $label = one.lib.form.label("Destination MAC Address"); + var $input = one.lib.form.input("7c:d1:c3:e8:e6:99"); + $input.attr('id', one.f.flows.id.modal.form.dstMac); + $fieldset.append($label).append($input); + if(edit) { + $input.val(existingFlow.dstMac); + } + // layer 3 + var $legend = one.lib.form.legend("Layer 3"); + $fieldset.append($legend); + + // srcIp + var $label = one.lib.form.label("Source IP Address"); + var $input = one.lib.form.input("192.168.3.128"); + $input.attr('id', one.f.flows.id.modal.form.srcIp); + $fieldset.append($label).append($input); + if(edit) { + $input.val(existingFlow.srcIp); + } + // dstIp + var $label = one.lib.form.label("Destination IP Address"); + var $input = one.lib.form.input("2001:2334::0/32"); + $input.attr('id', one.f.flows.id.modal.form.dstIp); + $fieldset.append($label).append($input); + if(edit) { + $input.val(existingFlow.dstIp); + } + // tosBits + var $label = one.lib.form.label("ToS Bits"); + var $input = one.lib.form.input("ToS Bits"); + $input.attr('id', one.f.flows.id.modal.form.tosBits); + var $help = one.lib.form.help("Range: 0 - 63"); + $fieldset.append($label).append($input).append($help); + if(edit) { + $input.val(existingFlow.tosBits); + } + + // layer 4 + var $legend = one.lib.form.legend("Layer 4"); + $fieldset.append($legend); + // srcPort + var $label = one.lib.form.label("Source Port"); + var $input = one.lib.form.input("Source Port"); + $input.attr('id', one.f.flows.id.modal.form.srcPort); + var $help = one.lib.form.help("Range: 0 - 65535"); + $fieldset.append($label).append($input).append($help); + if(edit) { + $input.val(existingFlow.srcPort); + } + // dstPort + var $label = one.lib.form.label("Destination Port"); + var $input = one.lib.form.input("Destination Port"); + $input.attr('id', one.f.flows.id.modal.form.dstPort); + var $help = one.lib.form.help("Range: 0 - 65535"); + $fieldset.append($label).append($input).append($help); + if(edit) { + $input.val(existingFlow.dstPort); + } + // protocol + var $label = one.lib.form.label("Protocol"); + var $input = one.lib.form.input("Protocol"); + $input.attr('id', one.f.flows.id.modal.form.protocol); + $fieldset.append($label).append($input); + if(edit) { + $input.val(existingFlow.protocol); + } + // actions + var $legend = one.lib.form.label("Actions"); + $fieldset.append($legend); + // actions table + var tableAttributes = ["table-striped", "table-bordered", "table-condensed", "table-hover", "table-cursor"]; + var $table = one.lib.dashlet.table.table(tableAttributes); + $table.attr('id', one.f.flows.id.modal.action.table); + var tableHeaders = ["Action", "Data"]; + var $thead = one.lib.dashlet.table.header(tableHeaders); + var $tbody = one.lib.dashlet.table.body("", tableHeaders); + $table.append($thead).append($tbody); + // actions + var actions = { + "" : "Please Select an Action", + "DROP" : "Drop", + "LOOPBACK" : "Loopback", + "FLOOD" : "Flood", + "FLOOD_ALL" : "Flood All", + "CONTROLLER" : "Controller", + "SW_PATH" : "Software Path", + "HW_PATH" : "Hardware Path", + "OUTPUT" : "Add Output Ports", + "ENQUEUE" : "Enqueue", + "SET_VLAN_ID" : "Set VLAN ID", + "SET_VLAN_PCP" : "Set VLAN Priority", + "SET_VLAN_CFI" : "Set VLAN CFI", + "POP_VLAN" : "Strip VLAN Header", + "PUSH_VLAN" : "Push VLAN", + "SET_DL_SRC" : "Modify Datalayer Source Address", + "SET_DL_DST" : "Modify Datalayer Destination Address", + "SET_DL_TYPE" : "Set Ethertype", + "SET_NW_SRC" : "Modify Network Source Address", + "SET_NW_DST" :"Modify Network Destination Address", + "SET_NW_TOS" : "Modify ToS Bits", + "SET_TP_SRC" : "Modify Transport Source Port", + "SET_TP_DST" : "Modify Transport Destination Port" + }; + var $select = one.lib.form.select.create(actions); + $select.attr('id', one.f.flows.id.modal.form.action); + // when selecting an action + $select.change(function() { + var action = $(this).find('option:selected'); + one.f.flows.modal.action.parse(action.attr('value')); + $select[0].selectedIndex = 0; + }); + + if(edit) { + $(existingFlow.actions).each(function(index, value){ + setTimeout(function(){ + var locEqualTo = value.indexOf("="); + if ( locEqualTo == -1 ) { + one.f.flows.modal.action.add.add(actions[value], value); } else { - resource['action'] = 'add'; + var action = value.substr(0,locEqualTo); + if( action == "OUTPUT") { + var portIds = value.substr(locEqualTo+1).split(","); + var ports = []; + var allPorts = one.f.flows.registry.nodeports[one.f.flows.registry.currentNode]['ports']; + for(var i =0; i < portIds.length ; i++) { + var portName = allPorts[portIds[i]]; + ports.push(portName); + } + one.f.flows.modal.action.add.addPortsToTable(ports.join(", "), portIds.join(",")); + } else { + var val = value.substr(locEqualTo+1); + one.f.flows.modal.action.add.addDataToTable(actions[action], val, action) + } } + }, 1000) + }); + } + $fieldset.append($select).append($table); - resource['nodeId'] = nodeId; - - if (edit) { - one.f.flows.modal.ajax.saveflow(resource, function(data) { - if (data == "Success") { - $modal.modal('hide').on('hidden', function () { - one.f.flows.detail(result['name'], nodeId); - }); - one.lib.alert('Flow Entry edited'); - one.main.dashlet.left.top.empty(); - one.f.flows.dashlet(one.main.dashlet.left.top); - } else { - alert('Could not edit flow: '+data); - } - }); - } else { - one.f.flows.modal.ajax.saveflow(resource, function(data) { - if (data == "Success") { - $modal.modal('hide'); - one.lib.alert('Flow Entry added'); - one.main.dashlet.left.top.empty(); - one.f.flows.dashlet(one.main.dashlet.left.top); - } else { - alert('Could not add flow: '+data); - } - }); - } + // return + $form.append($fieldset); + return $form; + }, + action : { + parse : function(option) { + switch (option) { + case "OUTPUT" : + var h3 = "Add Output Port"; + var $modal = one.f.flows.modal.action.initialize(h3, one.f.flows.modal.action.body.addOutputPorts, one.f.flows.modal.action.add.addOutputPorts); + $modal.modal(); + break; + case "SET_VLAN_ID" : + var h3 = "Set VLAN ID"; + var placeholder = "VLAN Identification Number"; + var id = one.f.flows.id.modal.action.setVlanId; + var help = "Range: 0 - 4095"; + var action = 'SET_VLAN_ID'; + var name = "VLAN ID"; + var body = function() { + return one.f.flows.modal.action.body.set(h3, placeholder, id, help); + }; + var add = function($modal) { + one.f.flows.modal.action.add.set(name, id, action, $modal); + }; + var $modal = one.f.flows.modal.action.initialize(h3, body, add); + $modal.modal(); + break; + case "SET_VLAN_PCP" : + var h3 = "Set VLAN Priority"; + var placeholder = "VLAN Priority"; + var id = one.f.flows.id.modal.action.setVlanPriority; + var help = "Range: 0 - 7"; + var action = 'SET_VLAN_PCP'; + var name = "VLAN Priority"; + var body = function() { + return one.f.flows.modal.action.body.set(h3, placeholder, id, help); + }; + var add = function($modal) { + one.f.flows.modal.action.add.set(name, id, action, $modal); + }; + var $modal = one.f.flows.modal.action.initialize(h3, body, add); + $modal.modal(); + break; + case "SET_VLAN_CFI" : + var h3 = "Set VLAN CFI"; + var placeholder = "VLAN CFI"; + var id = one.f.flows.id.modal.action.setVlanCfi; + var help = "Range: 0 - 1"; + var action = 'SET_VLAN_CFI'; + var name = "VLAN CFI"; + var body = function() { + return one.f.flows.modal.action.body.set(h3, placeholder, id, help); + }; + var add = function($modal) { + one.f.flows.modal.action.add.set(name, id, action, $modal); + }; + var $modal = one.f.flows.modal.action.initialize(h3, body, add); + $modal.modal(); + break; + case "POP_VLAN" : + var name = "Strip VLAN Header"; + var action = 'POP_VLAN'; + one.f.flows.modal.action.add.add(name, action); + break; + case "PUSH_VLAN" : + var h3 = "Push VLAN"; + var placeholder = "VLAN"; + var id = one.f.flows.id.modal.action.pushVlan; + var help = "Range: 0 - 4095"; + var action = 'PUSH_VLAN'; + var name = "VLAN"; + var body = function() { + return one.f.flows.modal.action.body.set(h3, placeholder, id, help); + }; + var add = function($modal) { + one.f.flows.modal.action.add.set(name, id, action, $modal); + }; + var $modal = one.f.flows.modal.action.initialize(h3, body, add); + $modal.modal(); + break; + case "SET_DL_SRC" : + var h3 = "Set Source MAC Address"; + var placeholder = "Source MAC Address"; + var id = one.f.flows.id.modal.action.modifyDatalayerSourceAddress; + var help = "Example: 00:11:22:aa:bb:cc"; + var action = 'SET_DL_SRC'; + var name = "Source MAC"; + var body = function() { + return one.f.flows.modal.action.body.set(h3, placeholder, id, help); + }; + var add = function($modal) { + one.f.flows.modal.action.add.set(name, id, action, $modal); + }; + var $modal = one.f.flows.modal.action.initialize(h3, body, add); + $modal.modal(); + break; + case "SET_DL_DST" : + var h3 = "Set Destination MAC Address"; + var placeholder = "Destination MAC Address"; + var id = one.f.flows.id.modal.action.modifyDatalayerDestinationAddress; + var help = "Example: 00:11:22:aa:bb:cc"; + var action = 'SET_DL_DST'; + var name = "Destination MAC"; + var body = function() { + return one.f.flows.modal.action.body.set(h3, placeholder, id, help); + }; + var add = function($modal) { + one.f.flows.modal.action.add.set(name, id, action, $modal); + }; + var $modal = one.f.flows.modal.action.initialize(h3, body, add); + $modal.modal(); + break; + case "SET_DL_TYPE" : + var h3 = "Set Ethertype"; + var placeholder = "Ethertype"; + var id = one.f.flows.id.modal.action.setEthertype; + var help = "Range: 0 - 65535"; + var action = 'SET_DL_TYPE'; + var name = "Ethertype"; + var body = function() { + return one.f.flows.modal.action.body.set(h3, placeholder, id, help); + }; + var add = function($modal) { + one.f.flows.modal.action.add.set(name, id, action, $modal); + }; + var $modal = one.f.flows.modal.action.initialize(h3, body, add); + $modal.modal(); + break; + case "SET_NW_SRC" : + var h3 = "Set IP Source Address"; + var placeholder = "Source IP Address"; + var id = one.f.flows.id.modal.action.modifyNetworkSourceAddress; + var help = "Example: 127.0.0.1"; + var action = 'SET_NW_SRC'; + var name = "Source IP"; + var body = function() { + return one.f.flows.modal.action.body.set(h3, placeholder, id, help); + }; + var add = function($modal) { + one.f.flows.modal.action.add.set(name, id, action, $modal); + }; + var $modal = one.f.flows.modal.action.initialize(h3, body, add); + $modal.modal(); + break; + case "SET_NW_DST" : + var h3 = "Set IP Destination Address"; + var placeholder = "Destination IP Address"; + var id = one.f.flows.id.modal.action.modifyNetworkDestinationAddress; + var help = "Example: 127.0.0.1"; + var action = 'SET_NW_DST'; + var name = "Destination IP"; + var body = function() { + return one.f.flows.modal.action.body.set(h3, placeholder, id, help); + }; + var add = function($modal) { + one.f.flows.modal.action.add.set(name, id, action, $modal); + }; + var $modal = one.f.flows.modal.action.initialize(h3, body, add); + $modal.modal(); + break; + case "SET_NW_TOS" : + var h3 = "Set IPv4 ToS"; + var placeholder = "IPv4 ToS"; + var id = one.f.flows.id.modal.action.modifyTosBits; + var help = "Range: 0 - 63"; + var action = 'SET_NW_TOS'; + var name = "ToS Bits"; + var body = function() { + return one.f.flows.modal.action.body.set(h3, placeholder, id, help); + }; + var add = function($modal) { + one.f.flows.modal.action.add.set(name, id, action, $modal); + }; + var $modal = one.f.flows.modal.action.initialize(h3, body, add); + $modal.modal(); + break; + case "SET_TP_SRC" : + var h3 = "Set Transport Source Port"; + var placeholder = "Transport Source Port"; + var id = one.f.flows.id.modal.action.modifyTransportSourcePort; + var help = "Range: 1 - 65535"; + var action = 'SET_TP_SRC'; + var name = "Source Port"; + var body = function() { + return one.f.flows.modal.action.body.set(h3, placeholder, id, help); + }; + var add = function($modal) { + one.f.flows.modal.action.add.set(name, id, action, $modal); + }; + var $modal = one.f.flows.modal.action.initialize(h3, body, add); + $modal.modal(); + break; + case "SET_TP_DST" : + var h3 = "Set Transport Destination Port"; + var placeholder = "Transport Destination Port"; + var id = one.f.flows.id.modal.action.modifyTransportDestinationPort; + var help = "Range: 1 - 65535"; + var action = 'SET_TP_DST'; + var name = "Destination Port"; + var body = function() { + return one.f.flows.modal.action.body.set(h3, placeholder, id, help); + }; + var add = function($modal) { + one.f.flows.modal.action.add.set(name, id, action, $modal); + }; + var $modal = one.f.flows.modal.action.initialize(h3, body, add); + $modal.modal(); + break; + case "ENQUEUE" : + var h3 = "Enqueue"; + var placeholder = "Enqueue"; + var id = one.f.flows.id.modal.action.enqueue; + var $modal = one.f.flows.modal.action.initialize(h3, one.f.flows.modal.action.body.addEnqueue, one.f.flows.modal.action.add.addEnqueue); + $modal.modal(); + break; + case "DROP" : + var name = "Drop"; + var action = 'DROP'; + one.f.flows.modal.action.add.add(name, action); + break; + case "LOOPBACK" : + var name = "Loopback"; + var action = 'LOOPBACK'; + one.f.flows.modal.action.add.add(name, action); + break; + case "FLOOD" : + var name = "Flood"; + var action = 'FLOOD'; + one.f.flows.modal.action.add.add(name, action); + break; + case "FLOOD_ALL" : + var name = "Flood All"; + var action = 'FLOOD_ALL'; + one.f.flows.modal.action.add.add(name, action); + break; + case "SW_PATH" : + var name = "Software Path"; + var action = 'SW_PATH'; + one.f.flows.modal.action.add.add(name, action); + break; + case "HW_PATH" : + var name = "Hardware Path"; + var action = 'HW_PATH'; + one.f.flows.modal.action.add.add(name, action); + break; + case "CONTROLLER" : + var name = "Controller"; + var action = 'CONTROLLER'; + one.f.flows.modal.action.add.add(name, action); + break; + } + }, + initialize : function(h3, bodyCallback, addCallback) { + var footer = one.f.flows.modal.action.footer(); + var $body = bodyCallback(); + var $modal = one.lib.modal.spawn(one.f.flows.id.modal.action.modal, h3, $body, footer); + // bind close button + $('#'+one.f.flows.id.modal.action.close, $modal).click(function() { + $modal.modal('hide'); + }); + // bind add flow button + $('#'+one.f.flows.id.modal.action.add, $modal).click(function() { + addCallback($modal); + }); + return $modal; + }, + add : { + addOutputPorts : function($modal) { + var $options = $('#'+one.f.flows.id.modal.action.addOutputPorts).find('option:selected'); + var ports = ''; + var pid = ''; + $options.each(function(index, value) { + ports = ports+$(value).text()+", "; + pid = pid+$(value).attr('value')+","; + }); + ports = ports.slice(0,-2); + pid = pid.slice(0,-1); + one.f.flows.modal.action.add.addPortsToTable(ports, pid); + $modal.modal('hide'); }, - ajax : { - nodes : function(successCallback) { - $.getJSON(one.f.address.root+one.f.address.flows.nodes, function(data) { - var nodes = one.f.flows.modal.data.nodes(data); - var nodeports = data; - one.f.flows.registry['nodes'] = nodes; - one.f.flows.registry['nodeports'] = nodeports; - - successCallback(nodes, nodeports); - }); - }, - saveflow : function(resource, callback) { - $.post(one.f.address.root+one.f.address.flows.flow, resource, function(data) { - callback(data); - }); - }, - removeflow : function(id, node, callback) { - resource = {}; - resource['action'] = 'remove'; - $.post(one.f.address.root+one.f.address.flows.flow+'/'+node+'/'+id, resource, function(data) { - callback(data); - }); - }, - toggleflow : function(id, node, callback) { - resource = {}; - resource['action'] = 'toggle'; - $.post(one.f.address.root+one.f.address.flows.flow+'/'+node+'/'+id, resource, function(data) { - callback(data); - }); - } + addEnqueue : function($modal) { + var $options = $('#'+one.f.flows.id.modal.action.addOutputPorts).find('option:selected'); + var ports = ''; + var pid = ''; + $options.each(function(index, value) { + ports = ports+$(value).text()+", "; + pid = pid+$(value).attr('value')+","; + }); + var $input = $('#'+one.f.flows.id.modal.action.queue); + var queue = $input.val(); + ports = ports.slice(0,-2); + pid = pid.slice(0,-1); + one.f.flows.modal.action.add.addEnqueueToTable(ports, pid, queue); + $modal.modal('hide'); }, - data : { - nodes : function(data) { - result = {}; - $.each(data, function(key, value) { - result[key] = value['name']; - }); - return result; - } + addEnqueueToTable : function(ports, pid, queue) { + if (queue !== '' && queue >= 0) { + ports += ':'+queue; + } + var $tr = one.f.flows.modal.action.table.add("Enqueue", ports); + $tr.attr('id', 'ENQUEUE'); + if (queue !== '' && queue >= 0) { + $tr.data('action', 'ENQUEUE='+pid+':'+queue); + } else { + $tr.data('action', 'ENQUEUE='+pid+':0'); // default queue to 0 + } + $tr.click(function() { + one.f.flows.modal.action.add.modal.initialize(this); + }); + one.f.flows.modal.action.table.append($tr); }, - body : function(nodes, nodeports, edit) { - var $form = $(document.createElement('form')); - var $fieldset = $(document.createElement('fieldset')); - var existingFlow; - // flow description - var $legend = one.lib.form.legend(""); - $legend.css('visibility', 'hidden'); - $fieldset.append($legend); - // name - var $label = one.lib.form.label("Name"); - var $input = one.lib.form.input("Flow Name"); - $input.attr('id', one.f.flows.id.modal.form.name); - if(edit) { - $input.attr('disabled', 'disabled'); - var flows = one.f.flows.registry.flows; - $(flows).each(function(index, value) { - if (value.name == one.f.flows.registry.selectedId && value.nodeId == one.f.flows.registry.selectedNode) { - existingFlow = value.flow; - } - }); - $input.val(existingFlow.name); - } - - $fieldset.append($label).append($input); - // node - var $label = one.lib.form.label("Node"); - var $select = one.lib.form.select.create(nodes); - one.lib.form.select.prepend($select, { '' : 'Please Select a Node' }); - $select.val($select.find("option:first").val()); - $select.attr('id', one.f.flows.id.modal.form.nodes); - if(edit) { - $select.attr('disabled', 'disabled'); - $select.val(existingFlow.node.type + "|"+ existingFlow.node.nodeIDString); - } + addPortsToTable : function(ports, pid){ + var $tr = one.f.flows.modal.action.table.add("Add Output Ports", ports); + $tr.attr('id', 'OUTPUT'); + $tr.data('action', 'OUTPUT='+pid); + $tr.click(function() { + one.f.flows.modal.action.add.modal.initialize(this); + }); + one.f.flows.modal.action.table.append($tr); + }, + add : function(name, action) { + var $tr = one.f.flows.modal.action.table.add(name); + $tr.attr('id', action); + $tr.data('action', action); + $tr.click(function() { + one.f.flows.modal.action.add.modal.initialize(this); + }); + one.f.flows.modal.action.table.append($tr); + }, + set : function(name, id, action, $modal) { + var $input = $('#'+id); + var value = $input.val(); + one.f.flows.modal.action.add.addDataToTable(name,value,action) + $modal.modal('hide'); + }, + addDataToTable : function(name,value,action) { + var $tr = one.f.flows.modal.action.table.add(name, value); + $tr.attr('id', action); + $tr.data('action', action+'='+value); + $tr.click(function() { + one.f.flows.modal.action.add.modal.initialize(this); + }); + one.f.flows.modal.action.table.append($tr); + }, + remove : function(that) { + $(that).remove(); + var $table = $('#'+one.f.flows.id.modal.action.table); + if ($table.find('tbody').find('tr').size() == 0) { + var $tr = $(document.createElement('tr')); + var $td = $(document.createElement('td')); + $td.attr('colspan', '3'); + $tr.addClass('empty'); + $td.text('No data available'); + $tr.append($td); + $table.find('tbody').append($tr); + } + }, + modal : { + initialize : function(that) { + var h3 = "Remove Action"; + var footer = one.f.flows.modal.action.add.modal.footer(); + var $body = one.f.flows.modal.action.add.modal.body(); + var $modal = one.lib.modal.spawn(one.f.flows.id.modal.action.modal.modal, h3, $body, footer); + + // bind cancel button + $('#'+one.f.flows.id.modal.action.modal.cancel, $modal).click(function() { + $modal.modal('hide'); + }); - // bind onchange - $select.change(function() { - // retrieve port value - var node = $(this).find('option:selected').attr('value'); - var $ports = $('#'+one.f.flows.id.modal.form.port); - if (node == '') { - one.lib.form.select.inject($ports, {}); - return; - } - one.f.flows.registry['currentNode'] = node; - var ports = nodeports[node]['ports']; - one.lib.form.select.inject($ports, ports); - one.lib.form.select.prepend($ports, { '' : 'Please Select a Port' }); - $ports.val($ports.find("option:first").val()); - if(edit) { - $ports.val( existingFlow.ingressPort ); - } + // bind remove button + $('#'+one.f.flows.id.modal.action.modal.remove, $modal).click(function() { + one.f.flows.modal.action.add.remove(that); + $modal.modal('hide'); }); - $fieldset.append($label).append($select); - // input port - var $label = one.lib.form.label("Input Port"); - var $select = one.lib.form.select.create(); - - $select.attr('id', one.f.flows.id.modal.form.port); - $fieldset.append($label).append($select); - // priority - var $label = one.lib.form.label("Priority"); - var $input = one.lib.form.input("Priority"); - $input.attr('id', one.f.flows.id.modal.form.priority); - $input.val('500'); - $fieldset.append($label).append($input); - if(edit) { - $input.val(existingFlow.priority); - } - // hardTimeout - var $label = one.lib.form.label("Hard Timeout"); - var $input = one.lib.form.input("Hard Timeout"); - $input.attr('id', one.f.flows.id.modal.form.hardTimeout); - if(edit) { - $input.val(existingFlow.hardTimeout); - } - $fieldset.append($label).append($input); - - // idleTimeout - var $label = one.lib.form.label("Idle Timeout"); - var $input = one.lib.form.input("Idle Timeout"); - $input.attr('id', one.f.flows.id.modal.form.idleTimeout); - $fieldset.append($label).append($input); - if(edit) { - $input.val(existingFlow.idleTimeout); - } - // cookie - var $label = one.lib.form.label("Cookie"); - var $input = one.lib.form.input("Cookie"); - $input.attr('id', one.f.flows.id.modal.form.cookie); - $fieldset.append($label).append($input); - if(edit) { - $input.val(existingFlow.cookie); - } + $modal.modal(); + }, + body : function() { + var $p = $(document.createElement('p')); + $p.append("Remove this action?"); + return $p; + }, + footer : function() { + var footer = []; - // layer 2 - var $legend = one.lib.form.legend("Layer 2"); - $fieldset.append($legend); - // etherType - var $label = one.lib.form.label("Ethernet Type"); - var $input = one.lib.form.input("Ethernet Type"); - $input.attr('id', one.f.flows.id.modal.form.etherType); - $input.val('0x800'); - $fieldset.append($label).append($input); - if(edit) { - $input.val(existingFlow.etherType); - } - // vlanId - var $label = one.lib.form.label("VLAN Identification Number"); - var $input = one.lib.form.input("VLAN Identification Number"); - $input.attr('id', one.f.flows.id.modal.form.vlanId); - var $help = one.lib.form.help("Range: 0 - 4095"); - $fieldset.append($label).append($input).append($help); - if(edit) { - $input.val(existingFlow.vlanId); - } + var removeButton = one.lib.dashlet.button.single("Remove Action", one.f.flows.id.modal.action.modal.remove, "btn-danger", ""); + var $removeButton = one.lib.dashlet.button.button(removeButton); + footer.push($removeButton); - // vlanPriority - var $label = one.lib.form.label("VLAN Priority"); - var $input = one.lib.form.input("VLAN Priority"); - $input.attr('id', one.f.flows.id.modal.form.vlanPriority); - var $help = one.lib.form.help("Range: 0 - 7"); - $fieldset.append($label).append($input).append($help); - if(edit) { - $input.val(existingFlow.vlanPriority); - } + var cancelButton = one.lib.dashlet.button.single("Cancel", one.f.flows.id.modal.action.modal.cancel, "", ""); + var $cancelButton = one.lib.dashlet.button.button(cancelButton); + footer.push($cancelButton); - // srcMac - var $label = one.lib.form.label("Source MAC Address"); - var $input = one.lib.form.input("3c:97:0e:75:c3:f7"); - $input.attr('id', one.f.flows.id.modal.form.srcMac); - $fieldset.append($label).append($input); - if(edit) { - $input.val(existingFlow.srcMac); - } - // dstMac - var $label = one.lib.form.label("Destination MAC Address"); - var $input = one.lib.form.input("7c:d1:c3:e8:e6:99"); - $input.attr('id', one.f.flows.id.modal.form.dstMac); - $fieldset.append($label).append($input); - if(edit) { - $input.val(existingFlow.dstMac); - } - // layer 3 - var $legend = one.lib.form.legend("Layer 3"); - $fieldset.append($legend); - - // srcIp - var $label = one.lib.form.label("Source IP Address"); - var $input = one.lib.form.input("192.168.3.128"); - $input.attr('id', one.f.flows.id.modal.form.srcIp); - $fieldset.append($label).append($input); - if(edit) { - $input.val(existingFlow.srcIp); - } - // dstIp - var $label = one.lib.form.label("Destination IP Address"); - var $input = one.lib.form.input("2001:2334::0/32"); - $input.attr('id', one.f.flows.id.modal.form.dstIp); - $fieldset.append($label).append($input); - if(edit) { - $input.val(existingFlow.dstIp); - } - // tosBits - var $label = one.lib.form.label("ToS Bits"); - var $input = one.lib.form.input("ToS Bits"); - $input.attr('id', one.f.flows.id.modal.form.tosBits); - var $help = one.lib.form.help("Range: 0 - 63"); - $fieldset.append($label).append($input).append($help); - if(edit) { - $input.val(existingFlow.tosBits); - } + return footer; + } + } + }, + table : { + add : function(action, data) { + var $tr = $(document.createElement('tr')); + var $td = $(document.createElement('td')); + $td.append(action); + $tr.append($td); + var $td = $(document.createElement('td')); + if (data != undefined) $td.append(data); + $tr.append($td); + return $tr; + }, + append : function($tr) { + var $table = $('#'+one.f.flows.id.modal.action.table); + var $empty = $table.find('.empty').parent(); + if ($empty.size() > 0) $empty.remove(); + $table.append($tr); + } + }, + body : { + common : function() { + var $form = $(document.createElement('form')); + var $fieldset = $(document.createElement('fieldset')); + return [$form, $fieldset]; + }, + addOutputPorts : function() { + var common = one.f.flows.modal.action.body.common(); + var $form = common[0]; + var $fieldset = common[1]; + // output port + $label = one.lib.form.label("Select Output Ports"); + if (one.f.flows.registry.currentNode == undefined){ + return; //Selecting Output ports without selecting node throws an exception + } + var ports = one.f.flows.registry.nodeports[one.f.flows.registry.currentNode]['ports']; + $select = one.lib.form.select.create(ports, true); + $select.attr('id', one.f.flows.id.modal.action.addOutputPorts); + $fieldset.append($label).append($select); + $form.append($fieldset); + return $form; + }, + addEnqueue : function() { + var common = one.f.flows.modal.action.body.common(); + var $form = common[0]; + var $fieldset = common[1]; + // output port + $label = one.lib.form.label("Select Output Ports"); + if (one.f.flows.registry.currentNode == undefined){ + return; //Selecting Output ports without selecting node throws an exception + } + var ports = one.f.flows.registry.nodeports[one.f.flows.registry.currentNode]['ports']; + $select = one.lib.form.select.create(ports); + $select.attr('id', one.f.flows.id.modal.action.addOutputPorts); + $fieldset.append($label).append($select); + $label = one.lib.form.label('Queue (Optional)'); + $input = one.lib.form.input('Queue') + .attr('id', one.f.flows.id.modal.action.queue); + $help = one.lib.form.help('Range: 1 - 2147483647'); + $fieldset.append($label).append($input).append($help); + $form.append($fieldset); + return $form; + }, + set : function(label, placeholder, id, help) { + var common = one.f.flows.modal.action.body.common(); + var $form = common[0]; + var $fieldset = common[1]; + // input + $label = one.lib.form.label(label); + $input = one.lib.form.input(placeholder); + $input.attr('id', id); + $help = one.lib.form.help(help); + // append + $fieldset.append($label).append($input).append($help); + $form.append($fieldset); + return $form; + } + }, + footer : function() { + var footer = []; + var addButton = one.lib.dashlet.button.single("Add Action", one.f.flows.id.modal.action.add, "btn-primary", ""); + var $addButton = one.lib.dashlet.button.button(addButton); + footer.push($addButton); + + var closeButton = one.lib.dashlet.button.single("Close", one.f.flows.id.modal.action.close, "", ""); + var $closeButton = one.lib.dashlet.button.button(closeButton); + footer.push($closeButton); + + return footer; + } + }, + footer : function() { + var footer = []; - // layer 4 - var $legend = one.lib.form.legend("Layer 4"); - $fieldset.append($legend); - // srcPort - var $label = one.lib.form.label("Source Port"); - var $input = one.lib.form.input("Source Port"); - $input.attr('id', one.f.flows.id.modal.form.srcPort); - var $help = one.lib.form.help("Range: 0 - 65535"); - $fieldset.append($label).append($input).append($help); - if(edit) { - $input.val(existingFlow.srcPort); - } - // dstPort - var $label = one.lib.form.label("Destination Port"); - var $input = one.lib.form.input("Destination Port"); - $input.attr('id', one.f.flows.id.modal.form.dstPort); - var $help = one.lib.form.help("Range: 0 - 65535"); - $fieldset.append($label).append($input).append($help); - if(edit) { - $input.val(existingFlow.dstPort); - } - // protocol - var $label = one.lib.form.label("Protocol"); - var $input = one.lib.form.input("Protocol"); - $input.attr('id', one.f.flows.id.modal.form.protocol); - $fieldset.append($label).append($input); - if(edit) { - $input.val(existingFlow.protocol); - } - // actions - var $legend = one.lib.form.label("Actions"); - $fieldset.append($legend); - // actions table - var tableAttributes = ["table-striped", "table-bordered", "table-condensed", "table-hover", "table-cursor"]; - var $table = one.lib.dashlet.table.table(tableAttributes); - $table.attr('id', one.f.flows.id.modal.action.table); - var tableHeaders = ["Action", "Data"]; - var $thead = one.lib.dashlet.table.header(tableHeaders); - var $tbody = one.lib.dashlet.table.body("", tableHeaders); - $table.append($thead).append($tbody); - // actions - var actions = { - "" : "Please Select an Action", - "DROP" : "Drop", - "LOOPBACK" : "Loopback", - "FLOOD" : "Flood", - "SW_PATH" : "Software Path", - "HW_PATH" : "Hardware Path", - "CONTROLLER" : "Controller", - "OUTPUT" : "Add Output Ports", - "SET_VLAN_ID" : "Set VLAN ID", - "SET_VLAN_PCP" : "Set VLAN Priority", - "POP_VLAN" : "Strip VLAN Header", - "SET_DL_SRC" : "Modify Datalayer Source Address", - "SET_DL_DST" : "Modify Datalayer Destination Address", - "SET_NW_SRC" : "Modify Network Source Address", - "SET_NW_DST" :"Modify Network Destination Address", - "SET_NW_TOS" : "Modify ToS Bits", - "SET_TP_SRC" : "Modify Transport Source Port", - "SET_TP_DST" : "Modify Transport Destination Port" - }; - var $select = one.lib.form.select.create(actions); - // when selecting an action - $select.change(function() { - var action = $(this).find('option:selected'); - one.f.flows.modal.action.parse(action.attr('value')); - $select[0].selectedIndex = 0; - }); + var installButton = one.lib.dashlet.button.single("Install Flow", one.f.flows.id.modal.install, "btn-success", ""); + var $installButton = one.lib.dashlet.button.button(installButton); + footer.push($installButton); - if(edit) { - $(existingFlow.actions).each(function(index, value){ - setTimeout(function(){ - var locEqualTo = value.indexOf("="); - if ( locEqualTo == -1 ) { - one.f.flows.modal.action.add.add(actions[value], value); - } else { - var action = value.substr(0,locEqualTo); - if( action == "OUTPUT") { - var portIds = value.substr(locEqualTo+1).split(","); - var ports = []; - var allPorts = one.f.flows.registry.nodeports[one.f.flows.registry.currentNode]['ports']; - for(var i =0; i < portIds.length ; i++) { - var portName = allPorts[portIds[i]]; - ports.push(portName); - } - one.f.flows.modal.action.add.addPortsToTable(ports.join(", "), portIds.join(",")); - } else { - var val = value.substr(locEqualTo+1); - one.f.flows.modal.action.add.addDataToTable(actions[action], val, action) - } - } - }, 1000) - }); - } - $fieldset.append($select).append($table); + var addButton = one.lib.dashlet.button.single("Save Flow", one.f.flows.id.modal.add, "btn-primary", ""); + var $addButton = one.lib.dashlet.button.button(addButton); + footer.push($addButton); - // return - $form.append($fieldset); - return $form; - }, - action : { - parse : function(option) { - switch (option) { - case "OUTPUT" : - var h3 = "Add Output Port"; - var $modal = one.f.flows.modal.action.initialize(h3, one.f.flows.modal.action.body.addOutputPorts, one.f.flows.modal.action.add.addOutputPorts); - $modal.modal(); - break; - case "SET_VLAN_ID" : - var h3 = "Set VLAN ID"; - var placeholder = "VLAN Identification Number"; - var id = one.f.flows.id.modal.action.setVlanId; - var help = "Range: 0 - 4095"; - var action = 'SET_VLAN_ID'; - var name = "VLAN ID"; - var body = function() { - return one.f.flows.modal.action.body.set(h3, placeholder, id, help); - }; - var add = function($modal) { - one.f.flows.modal.action.add.set(name, id, action, $modal); - }; - var $modal = one.f.flows.modal.action.initialize(h3, body, add); - $modal.modal(); - break; - case "SET_VLAN_PCP" : - var h3 = "Set VLAN Priority"; - var placeholder = "VLAN Priority"; - var id = one.f.flows.id.modal.action.setVlanPriority; - var help = "Range: 0 - 7"; - var action = 'SET_VLAN_PCP'; - var name = "VLAN Priority"; - var body = function() { - return one.f.flows.modal.action.body.set(h3, placeholder, id, help); - }; - var add = function($modal) { - one.f.flows.modal.action.add.set(name, id, action, $modal); - }; - var $modal = one.f.flows.modal.action.initialize(h3, body, add); - $modal.modal(); - break; - case "POP_VLAN" : - var name = "Strip VLAN Header"; - var action = 'POP_VLAN'; - one.f.flows.modal.action.add.add(name, action); - break; - case "SET_DL_SRC" : - var h3 = "Set Source MAC Address"; - var placeholder = "Source MAC Address"; - var id = one.f.flows.id.modal.action.modifyDatalayerSourceAddress; - var help = "Example: 00:11:22:aa:bb:cc"; - var action = 'SET_DL_SRC'; - var name = "Source MAC"; - var body = function() { - return one.f.flows.modal.action.body.set(h3, placeholder, id, help); - }; - var add = function($modal) { - one.f.flows.modal.action.add.set(name, id, action, $modal); - }; - var $modal = one.f.flows.modal.action.initialize(h3, body, add); - $modal.modal(); - break; - case "SET_DL_DST" : - var h3 = "Set Destination MAC Address"; - var placeholder = "Destination MAC Address"; - var id = one.f.flows.id.modal.action.modifyDatalayerDestinationAddress; - var help = "Example: 00:11:22:aa:bb:cc"; - var action = 'SET_DL_DST'; - var name = "Destination MAC"; - var body = function() { - return one.f.flows.modal.action.body.set(h3, placeholder, id, help); - }; - var add = function($modal) { - one.f.flows.modal.action.add.set(name, id, action, $modal); - }; - var $modal = one.f.flows.modal.action.initialize(h3, body, add); - $modal.modal(); - break; - case "SET_NW_SRC" : - var h3 = "Set IP Source Address"; - var placeholder = "Source IP Address"; - var id = one.f.flows.id.modal.action.modifyNetworkSourceAddress; - var help = "Example: 127.0.0.1"; - var action = 'SET_NW_SRC'; - var name = "Source IP"; - var body = function() { - return one.f.flows.modal.action.body.set(h3, placeholder, id, help); - }; - var add = function($modal) { - one.f.flows.modal.action.add.set(name, id, action, $modal); - }; - var $modal = one.f.flows.modal.action.initialize(h3, body, add); - $modal.modal(); - break; - case "SET_NW_DST" : - var h3 = "Set IP Destination Address"; - var placeholder = "Destination IP Address"; - var id = one.f.flows.id.modal.action.modifyNetworkDestinationAddress; - var help = "Example: 127.0.0.1"; - var action = 'SET_NW_DST'; - var name = "Destination IP"; - var body = function() { - return one.f.flows.modal.action.body.set(h3, placeholder, id, help); - }; - var add = function($modal) { - one.f.flows.modal.action.add.set(name, id, action, $modal); - }; - var $modal = one.f.flows.modal.action.initialize(h3, body, add); - $modal.modal(); - break; - case "SET_NW_TOS" : - var h3 = "Set IPv4 ToS"; - var placeholder = "IPv4 ToS"; - var id = one.f.flows.id.modal.action.modifyTosBits; - var help = "Range: 0 - 63"; - var action = 'SET_NW_TOS'; - var name = "ToS Bits"; - var body = function() { - return one.f.flows.modal.action.body.set(h3, placeholder, id, help); - }; - var add = function($modal) { - one.f.flows.modal.action.add.set(name, id, action, $modal); - }; - var $modal = one.f.flows.modal.action.initialize(h3, body, add); - $modal.modal(); - break; - case "SET_TP_SRC" : - var h3 = "Set Transport Source Port"; - var placeholder = "Transport Source Port"; - var id = one.f.flows.id.modal.action.modifyTransportSourcePort; - var help = "Range: 1 - 65535"; - var action = 'SET_TP_SRC'; - var name = "Source Port"; - var body = function() { - return one.f.flows.modal.action.body.set(h3, placeholder, id, help); - }; - var add = function($modal) { - one.f.flows.modal.action.add.set(name, id, action, $modal); - }; - var $modal = one.f.flows.modal.action.initialize(h3, body, add); - $modal.modal(); - break; - case "SET_TP_DST" : - var h3 = "Set Transport Destination Port"; - var placeholder = "Transport Destination Port"; - var id = one.f.flows.id.modal.action.modifyTransportDestinationPort; - var help = "Range: 1 - 65535"; - var action = 'SET_TP_DST'; - var name = "Destination Port"; - var body = function() { - return one.f.flows.modal.action.body.set(h3, placeholder, id, help); - }; - var add = function($modal) { - one.f.flows.modal.action.add.set(name, id, action, $modal); - }; - var $modal = one.f.flows.modal.action.initialize(h3, body, add); - $modal.modal(); - break; - case "DROP" : - var name = "Drop"; - var action = 'DROP'; - one.f.flows.modal.action.add.add(name, action); - break; - case "LOOPBACK" : - var name = "Loopback"; - var action = 'LOOPBACK'; - one.f.flows.modal.action.add.add(name, action); - break; - case "FLOOD" : - var name = "Flood"; - var action = 'FLOOD'; - one.f.flows.modal.action.add.add(name, action); - break; - case "SW_PATH" : - var name = "Software Path"; - var action = 'SW_PATH'; - one.f.flows.modal.action.add.add(name, action); - break; - case "HW_PATH" : - var name = "Hardware Path"; - var action = 'HW_PATH'; - one.f.flows.modal.action.add.add(name, action); - break; - case "CONTROLLER" : - var name = "Controller"; - var action = 'CONTROLLER'; - one.f.flows.modal.action.add.add(name, action); - break; - } - }, - initialize : function(h3, bodyCallback, addCallback) { - var footer = one.f.flows.modal.action.footer(); - var $body = bodyCallback(); - var $modal = one.lib.modal.spawn(one.f.flows.id.modal.action.modal, h3, $body, footer); - // bind close button - $('#'+one.f.flows.id.modal.action.close, $modal).click(function() { - $modal.modal('hide'); - }); - // bind add flow button - $('#'+one.f.flows.id.modal.action.add, $modal).click(function() { - addCallback($modal); - }); - return $modal; - }, - add : { - addOutputPorts : function($modal) { - var $options = $('#'+one.f.flows.id.modal.action.addOutputPorts).find('option:selected'); - var ports = ''; - var pid = ''; - $options.each(function(index, value) { - ports = ports+$(value).text()+", "; - pid = pid+$(value).attr('value')+","; - }); - ports = ports.slice(0,-2); - pid = pid.slice(0,-1); - one.f.flows.modal.action.add.addPortsToTable(ports, pid); - $modal.modal('hide'); - }, - addPortsToTable : function(ports, pid){ - var $tr = one.f.flows.modal.action.table.add("Add Output Ports", ports); - $tr.attr('id', 'OUTPUT'); - $tr.data('action', 'OUTPUT='+pid); - $tr.click(function() { - one.f.flows.modal.action.add.modal.initialize(this); - }); - one.f.flows.modal.action.table.append($tr); - }, - add : function(name, action) { - var $tr = one.f.flows.modal.action.table.add(name); - $tr.attr('id', action); - $tr.data('action', action); - $tr.click(function() { - one.f.flows.modal.action.add.modal.initialize(this); - }); - one.f.flows.modal.action.table.append($tr); - }, - set : function(name, id, action, $modal) { - var $input = $('#'+id); - var value = $input.val(); - one.f.flows.modal.action.add.addDataToTable(name,value,action) - $modal.modal('hide'); - }, - addDataToTable : function(name,value,action) { - var $tr = one.f.flows.modal.action.table.add(name, value); - $tr.attr('id', action); - $tr.data('action', action+'='+value); - $tr.click(function() { - one.f.flows.modal.action.add.modal.initialize(this); - }); - one.f.flows.modal.action.table.append($tr); - }, - remove : function(that) { - $(that).remove(); - var $table = $('#'+one.f.flows.id.modal.action.table); - if ($table.find('tbody').find('tr').size() == 0) { - var $tr = $(document.createElement('tr')); - var $td = $(document.createElement('td')); - $td.attr('colspan', '3'); - $tr.addClass('empty'); - $td.text('No data available'); - $tr.append($td); - $table.find('tbody').append($tr); - } - }, - modal : { - initialize : function(that) { - var h3 = "Remove Action"; - var footer = one.f.flows.modal.action.add.modal.footer(); - var $body = one.f.flows.modal.action.add.modal.body(); - var $modal = one.lib.modal.spawn(one.f.flows.id.modal.action.modal.modal, h3, $body, footer); - - // bind cancel button - $('#'+one.f.flows.id.modal.action.modal.cancel, $modal).click(function() { - $modal.modal('hide'); - }); - - // bind remove button - $('#'+one.f.flows.id.modal.action.modal.remove, $modal).click(function() { - one.f.flows.modal.action.add.remove(that); - $modal.modal('hide'); - }); - - $modal.modal(); - }, - body : function() { - var $p = $(document.createElement('p')); - $p.append("Remove this action?"); - return $p; - }, - footer : function() { - var footer = []; - - var removeButton = one.lib.dashlet.button.single("Remove Action", one.f.flows.id.modal.action.modal.remove, "btn-danger", ""); - var $removeButton = one.lib.dashlet.button.button(removeButton); - footer.push($removeButton); - - var cancelButton = one.lib.dashlet.button.single("Cancel", one.f.flows.id.modal.action.modal.cancel, "", ""); - var $cancelButton = one.lib.dashlet.button.button(cancelButton); - footer.push($cancelButton); - - return footer; - } - } - }, - table : { - add : function(action, data) { - var $tr = $(document.createElement('tr')); - var $td = $(document.createElement('td')); - $td.append(action); - $tr.append($td); - var $td = $(document.createElement('td')); - if (data != undefined) $td.append(data); - $tr.append($td); - return $tr; - }, - append : function($tr) { - var $table = $('#'+one.f.flows.id.modal.action.table); - var $empty = $table.find('.empty').parent(); - if ($empty.size() > 0) $empty.remove(); - $table.append($tr); - } - }, - body : { - common : function() { - var $form = $(document.createElement('form')); - var $fieldset = $(document.createElement('fieldset')); - return [$form, $fieldset]; - }, - addOutputPorts : function() { - var common = one.f.flows.modal.action.body.common(); - var $form = common[0]; - var $fieldset = common[1]; - // output port - $label = one.lib.form.label("Select Output Ports"); - if (one.f.flows.registry.currentNode == undefined){ - return; //Selecting Output ports without selecting node throws an exception - } - var ports = one.f.flows.registry.nodeports[one.f.flows.registry.currentNode]['ports']; - $select = one.lib.form.select.create(ports, true); - $select.attr('id', one.f.flows.id.modal.action.addOutputPorts); - $fieldset.append($label).append($select); - $form.append($fieldset); - return $form; - }, - set : function(label, placeholder, id, help) { - var common = one.f.flows.modal.action.body.common(); - var $form = common[0]; - var $fieldset = common[1]; - // input - $label = one.lib.form.label(label); - $input = one.lib.form.input(placeholder); - $input.attr('id', id); - $help = one.lib.form.help(help); - // append - $fieldset.append($label).append($input).append($help); - $form.append($fieldset); - return $form; - } - }, - footer : function() { - var footer = []; - var addButton = one.lib.dashlet.button.single("Add Action", one.f.flows.id.modal.action.add, "btn-primary", ""); - var $addButton = one.lib.dashlet.button.button(addButton); - footer.push($addButton); - - var closeButton = one.lib.dashlet.button.single("Close", one.f.flows.id.modal.action.close, "", ""); - var $closeButton = one.lib.dashlet.button.button(closeButton); - footer.push($closeButton); - - return footer; - } - }, - footer : function() { - var footer = []; + var closeButton = one.lib.dashlet.button.single("Close", one.f.flows.id.modal.close, "", ""); + var $closeButton = one.lib.dashlet.button.button(closeButton); + footer.push($closeButton); - var installButton = one.lib.dashlet.button.single("Install Flow", one.f.flows.id.modal.install, "btn-success", ""); - var $installButton = one.lib.dashlet.button.button(installButton); - footer.push($installButton); + return footer; + }, + footerEdit : function() { + var footer = []; - var addButton = one.lib.dashlet.button.single("Save Flow", one.f.flows.id.modal.add, "btn-primary", ""); - var $addButton = one.lib.dashlet.button.button(addButton); - footer.push($addButton); + var editButton = one.lib.dashlet.button.single("Save Flow", one.f.flows.id.modal.edit, "btn-success", ""); + var $editButton = one.lib.dashlet.button.button(editButton); + footer.push($editButton); - var closeButton = one.lib.dashlet.button.single("Close", one.f.flows.id.modal.close, "", ""); - var $closeButton = one.lib.dashlet.button.button(closeButton); - footer.push($closeButton); + var closeButton = one.lib.dashlet.button.single("Close", one.f.flows.id.modal.close, "", ""); + var $closeButton = one.lib.dashlet.button.button(closeButton); + footer.push($closeButton); - return footer; - }, - footerEdit : function() { - var footer = []; + return footer; + }, + removeMultiple: { + dialog: function(flows) { + var h3 = 'Remove Flow Entry'; + var flowList = []; + for (var i = 0; i < flows.length; i++) { + flowList.push(flows[i]["name"]); + } + var footer = one.f.flows.modal.removeMultiple.footer(); + var $body = one.f.flows.modal.removeMultiple.body(flowList); + var $modal = one.lib.modal.spawn(one.f.flows.id.modal.dialog.modal, h3, $body, footer); - var editButton = one.lib.dashlet.button.single("Save Flow", one.f.flows.id.modal.edit, "btn-success", ""); - var $editButton = one.lib.dashlet.button.button(editButton); - footer.push($editButton); + // bind close button + $('#'+one.f.flows.id.modal.dialog.close, $modal).click(function() { + $modal.modal('hide'); + }); - var closeButton = one.lib.dashlet.button.single("Close", one.f.flows.id.modal.close, "", ""); - var $closeButton = one.lib.dashlet.button.button(closeButton); - footer.push($closeButton); + // bind remove rule button + $('#'+one.f.flows.id.modal.dialog.remove, $modal).click(this, function(e) { + var resource = {}; + resource['body'] = JSON.stringify(flows); - return footer; - }, - removeMultiple: { - dialog: function(flows) { - var h3 = 'Remove Flow Entry'; - var flowList = []; - for (var i = 0; i < flows.length; i++) { - flowList.push(flows[i]["name"]); - } - var footer = one.f.flows.modal.removeMultiple.footer(); - var $body = one.f.flows.modal.removeMultiple.body(flowList); - var $modal = one.lib.modal.spawn(one.f.flows.id.modal.dialog.modal, h3, $body, footer); - - // bind close button - $('#'+one.f.flows.id.modal.dialog.close, $modal).click(function() { - $modal.modal('hide'); - }); - - // bind remove rule button - $('#'+one.f.flows.id.modal.dialog.remove, $modal).click(this, function(e) { - var resource = {}; - resource['body'] = JSON.stringify(flows); - - $.post(one.f.address.root+one.f.address.flows.deleteFlows, resource, function(response) { - $modal.modal('hide'); - if(response == "Success") { - one.lib.alert("Flow Entry(s) successfully removed"); - } else { - one.lib.alert(response); - } - one.main.dashlet.right.bottom.empty(); - one.f.detail.dashlet(one.main.dashlet.right.bottom); - one.main.dashlet.left.top.empty(); - one.f.flows.dashlet(one.main.dashlet.left.top); - }); - }); - $modal.modal(); - }, - footer : function() { - var footer = []; - var remove = one.lib.dashlet.button.single('Remove Flow Entry',one.f.flows.id.modal.dialog.remove, 'btn-danger', ''); - var $remove = one.lib.dashlet.button.button(remove); - footer.push($remove); - - var cancel = one.lib.dashlet.button.single('Cancel', one.f.flows.id.modal.dialog.close, '', ''); - var $cancel = one.lib.dashlet.button.button(cancel); - footer.push($cancel); - - return footer; - }, - body : function (flows) { - var $p = $(document.createElement('p')); - var p = 'Remove the following Flow Entry(s)?'; - //creata a BS label for each rule and append to list - $(flows).each(function(){ - var $span = $(document.createElement('span')); - $span.append(this); - p += '
    ' + $span[0].outerHTML; - }); - $p.append(p); - return $p; + $.post(one.f.address.root+one.f.address.flows.deleteFlows, resource, function(response) { + $modal.modal('hide'); + if(response == "Success") { + one.lib.alert("Flow Entry(s) successfully removed"); + } else { + one.lib.alert(response); } - } - }, - ajax : { - dashlet : function(callback) { - $.getJSON(one.f.address.root+one.f.address.flows.main, function(data) { - one.f.flows.registry['flows'] = data.flows; - one.f.flows.registry['privilege'] = data.privilege; - one.f.flows.modal.ajax.nodes(function(){/*Empty function. Do nothing. */}) + one.main.dashlet.right.bottom.empty(); + one.f.detail.dashlet(one.main.dashlet.right.bottom); + one.main.dashlet.left.top.empty(); + one.f.flows.dashlet(one.main.dashlet.left.top); + }); + }); + $modal.modal(); + }, + footer : function() { + var footer = []; + var remove = one.lib.dashlet.button.single('Remove Flow Entry',one.f.flows.id.modal.dialog.remove, 'btn-danger', ''); + var $remove = one.lib.dashlet.button.button(remove); + footer.push($remove); + + var cancel = one.lib.dashlet.button.single('Cancel', one.f.flows.id.modal.dialog.close, '', ''); + var $cancel = one.lib.dashlet.button.button(cancel); + footer.push($cancel); + + return footer; + }, + body : function (flows) { + var $p = $(document.createElement('p')); + var p = 'Remove the following Flow Entry(s)?'; + //creata a BS label for each rule and append to list + $(flows).each(function(){ + var $span = $(document.createElement('span')); + $span.append(this); + p += '
    ' + $span[0].outerHTML; + }); + $p.append(p); + return $p; + } + } + }, + ajax : { + dashlet : function(callback) { + $.getJSON(one.f.address.root+one.f.address.flows.main, function(data) { + one.f.flows.registry['flows'] = data.flows; + one.f.flows.registry['privilege'] = data.privilege; + one.f.flows.modal.ajax.nodes(function(){/*Empty function. Do nothing. */}) + + callback(data); + }); + } + }, + data : { + flowsDataGrid: function(data) { + var source = new StaticDataSource({ + columns: [ + { + property: 'selector', + label: "", + sortable: false + }, + { + property: 'name', + label: 'Flow Name', + sortable: true + }, + { + property: 'node', + label: 'Node', + sortable: true + } + ], + data: data.flows, + formatter: function(items) { + $.each(items, function(index, item) { + var $checkbox = document.createElement("input"); + $checkbox.setAttribute("type", "checkbox"); + $checkbox.setAttribute("name", item.name); + $checkbox.setAttribute("node", item.nodeId); + $checkbox.setAttribute('class','flowEntry') + item.selector = $checkbox.outerHTML; + item["name"] = '' + item["name"] + ''; + }); - callback(data); - }); - } - }, - data : { - flowsDataGrid: function(data) { - var source = new StaticDataSource({ - columns: [ - { - property: 'selector', - label: "", - sortable: false - }, - { - property: 'name', - label: 'Flow Name', - sortable: true - }, - { - property: 'node', - label: 'Node', - sortable: true - } - ], - data: data.flows, - formatter: function(items) { - $.each(items, function(index, item) { - var $checkbox = document.createElement("input"); - $checkbox.setAttribute("type", "checkbox"); - $checkbox.setAttribute("name", item.name); - $checkbox.setAttribute("node", item.nodeId); - $checkbox.setAttribute('class','flowEntry') - item.selector = $checkbox.outerHTML; - item["name"] = '' + item["name"] + ''; - }); - - }, - delay: 0 - }); - return source; - }, - dashlet : function(data) { - var body = []; - $(data).each(function(index, value) { - var tr = {}; - var entry = []; - - - entry.push(value['name']); - entry.push(value['node']); - if (value['flow']['installInHw'] == 'true' && value['flow']['status'] == 'Success') - tr['type'] = ['success']; - else if (value['flow']['installInHw'] == 'false' && value['flow']['status'] == 'Success') - tr['type'] = ['warning']; - else - tr['type'] = ['warning']; - tr['entry'] = entry; - tr['id'] = value['nodeId']; - - body.push(tr); - }); - return body; - } + }, + delay: 0 + }); + return source; }, - body : { - dashlet : function(body, callback) { - var attributes = ['table-striped', 'table-bordered', 'table-hover', 'table-condensed', 'table-cursor']; - var $table = one.lib.dashlet.table.table(attributes); - - var headers = ['Flow Name', 'Node']; - - var $thead = one.lib.dashlet.table.header(headers); - $table.append($thead); - - var $tbody = one.lib.dashlet.table.body(body); - $table.append($tbody); - return $table; - } + dashlet : function(data) { + var body = []; + $(data).each(function(index, value) { + var tr = {}; + var entry = []; + + + entry.push(value['name']); + entry.push(value['node']); + if (value['flow']['installInHw'] == 'true' && value['flow']['status'] == 'Success') + tr['type'] = ['success']; + else if (value['flow']['installInHw'] == 'false' && value['flow']['status'] == 'Success') + tr['type'] = ['warning']; + else + tr['type'] = ['warning']; + tr['entry'] = entry; + tr['id'] = value['nodeId']; + + body.push(tr); + }); + return body; } + }, + body : { + dashlet : function(body, callback) { + var attributes = ['table-striped', 'table-bordered', 'table-hover', 'table-condensed', 'table-cursor']; + var $table = one.lib.dashlet.table.table(attributes); + + var headers = ['Flow Name', 'Node']; + + var $thead = one.lib.dashlet.table.header(headers); + $table.append($thead); + + var $tbody = one.lib.dashlet.table.body(body); + $table.append($tbody); + return $table; + } + } } /** INIT **/ // populate nav tabs $(one.f.menu.left.top).each(function(index, value) { - var $nav = $(".nav", "#left-top"); - one.main.page.dashlet($nav, value); + var $nav = $(".nav", "#left-top"); + one.main.page.dashlet($nav, value); }); $(one.f.menu.left.bottom).each(function(index, value) { - var $nav = $(".nav", "#left-bottom"); - one.main.page.dashlet($nav, value); + var $nav = $(".nav", "#left-bottom"); + one.main.page.dashlet($nav, value); }); $(one.f.menu.right.bottom).each(function(index, value) { - var $nav = $(".nav", "#right-bottom"); - one.main.page.dashlet($nav, value); + var $nav = $(".nav", "#right-bottom"); + one.main.page.dashlet($nav, value); }); one.f.populate = function($dashlet, header) { - var $h4 = one.lib.dashlet.header(header); - $dashlet.append($h4); + var $h4 = one.lib.dashlet.header(header); + $dashlet.append($h4); }; // bind dashlet nav $('.dash .nav a', '#main').click(function() { - // de/activation - var $li = $(this).parent(); - var $ul = $li.parent(); - one.lib.nav.unfocus($ul); - $li.addClass('active'); - // clear respective dashlet - var $dashlet = $ul.parent().find('.dashlet'); - one.lib.dashlet.empty($dashlet); - // callback based on menu - var id = $(this).attr('id'); - var menu = one.f.dashlet; - switch (id) { - case menu.flows.id: - one.f.flows.dashlet($dashlet); - break; - case menu.nodes.id: - one.f.nodes.dashlet($dashlet); - break; - case menu.detail.id: - one.f.detail.dashlet($dashlet); - break; - }; + // de/activation + var $li = $(this).parent(); + var $ul = $li.parent(); + one.lib.nav.unfocus($ul); + $li.addClass('active'); + // clear respective dashlet + var $dashlet = $ul.parent().find('.dashlet'); + one.lib.dashlet.empty($dashlet); + // callback based on menu + var id = $(this).attr('id'); + var menu = one.f.dashlet; + switch (id) { + case menu.flows.id: + one.f.flows.dashlet($dashlet); + break; + case menu.nodes.id: + one.f.nodes.dashlet($dashlet); + break; + case menu.detail.id: + one.f.detail.dashlet($dashlet); + break; + }; }); // activate first tab on each dashlet $('.dash .nav').each(function(index, value) { - $($(value).find('li')[0]).find('a').click(); + $($(value).find('li')[0]).find('a').click(); });