summaryrefslogtreecommitdiffstats
path: root/framework/src/onos/core/net/src/main/java/org/onosproject
diff options
context:
space:
mode:
authorAshlee Young <ashlee@onosfw.com>2015-09-09 22:15:21 -0700
committerAshlee Young <ashlee@onosfw.com>2015-09-09 22:15:21 -0700
commit13d05bc8458758ee39cb829098241e89616717ee (patch)
tree22a4d1ce65f15952f07a3df5af4b462b4697cb3a /framework/src/onos/core/net/src/main/java/org/onosproject
parent6139282e1e93c2322076de4b91b1c85d0bc4a8b3 (diff)
ONOS checkin based on commit tag e796610b1f721d02f9b0e213cf6f7790c10ecd60
Change-Id: Ife8810491034fe7becdba75dda20de4267bd15cd
Diffstat (limited to 'framework/src/onos/core/net/src/main/java/org/onosproject')
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/app/impl/ApplicationManager.java250
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/app/impl/package-info.java20
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/cfg/impl/ComponentConfigManager.java281
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/cfg/impl/ConfigPropertyDefinitions.java81
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/cfg/impl/package-info.java20
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/cluster/impl/ClusterManager.java156
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/cluster/impl/MastershipManager.java282
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/cluster/impl/package-info.java20
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/core/impl/BlockAllocatorBasedIdGenerator.java65
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/core/impl/CoreManager.java190
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/core/impl/IdBlockAllocator.java38
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/core/impl/MetricsManagerComponent.java41
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/core/impl/StoreBasedIdBlockAllocator.java46
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/core/impl/package-info.java20
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/event/impl/CoreEventDispatcher.java175
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/event/impl/package-info.java20
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/config/impl/BasicNetworkConfigs.java115
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/config/impl/NetworkConfigLoader.java218
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/config/impl/NetworkConfigManager.java288
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/config/impl/package-info.java20
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/BasicDeviceOperator.java107
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/DeviceManager.java765
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/OpticalPortOperator.java173
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/package-info.java20
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/driver/impl/DriverManager.java188
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/driver/impl/package-info.java20
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/edgeservice/impl/EdgeManager.java241
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/edgeservice/impl/package-info.java4
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/flow/impl/FlowRuleManager.java593
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/flow/impl/package-info.java20
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/FlowObjectiveManager.java416
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FilterTable.java61
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionManager.java439
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionTree.java271
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionUtil.java488
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/ForwardTable.java109
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/ForwardUpdateTable.java46
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/NextTable.java61
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/package-info.java20
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/package-info.java20
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/group/impl/GroupManager.java318
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/group/impl/package-info.java20
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/host/impl/BasicHostOperator.java84
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/host/impl/HostManager.java300
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/host/impl/HostMonitor.java288
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/host/impl/package-info.java20
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/CompilerRegistry.java128
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/IntentAccumulator.java82
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/IntentCleanup.java254
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/IntentCompilationException.java37
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/IntentInstallationException.java37
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/IntentManager.java488
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/IntentProcessor.java46
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/IntentRemovalException.java37
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/ObjectiveTracker.java457
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/ObjectiveTrackerService.java69
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/PathNotFoundException.java46
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/TopologyChangeDelegate.java37
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/ConnectivityIntentCompiler.java152
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/HostToHostIntentCompiler.java110
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompiler.java138
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MplsIntentCompiler.java91
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MplsPathIntentCompiler.java291
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MultiPointToSinglePointIntentCompiler.java151
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalCircuitIntentCompiler.java372
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalConnectivityIntentCompiler.java305
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalPathIntentCompiler.java195
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathIntentCompiler.java116
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PointToPointIntentCompiler.java104
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/SinglePointToMultiPointIntentCompiler.java85
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/TwoWayP2PIntentCompiler.java72
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/package-info.java20
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/package-info.java21
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/Compiling.java73
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/Corrupt.java44
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/Failed.java44
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/FinalIntentProcessPhase.java44
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/InstallRequest.java55
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/Installing.java58
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/IntentProcessPhase.java73
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/IntentWorker.java52
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/PurgeRequest.java70
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/WithdrawRequest.java70
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/Withdrawing.java55
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/Withdrawn.java44
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/package-info.java20
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/link/impl/BasicLinkOperator.java86
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/link/impl/LinkManager.java343
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/link/impl/package-info.java20
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/newresource/impl/ResourceDeviceListener.java86
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/newresource/impl/ResourceLinkListener.java152
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/newresource/impl/ResourceManager.java148
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/newresource/impl/ResourceRegistrar.java73
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/newresource/impl/package-info.java20
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/packet/impl/PacketManager.java329
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/packet/impl/package-info.java22
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/proxyarp/impl/ProxyArpManager.java447
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/proxyarp/impl/package-info.java20
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/resource/impl/DeviceResourceManager.java104
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/resource/impl/LinkResourceManager.java293
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/resource/impl/package-info.java20
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/statistic/impl/StatisticManager.java379
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/statistic/impl/package-info.java20
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/topology/impl/DefaultTopologyProvider.java287
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/topology/impl/PathManager.java190
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/topology/impl/TopologyManager.java215
-rw-r--r--framework/src/onos/core/net/src/main/java/org/onosproject/net/topology/impl/package-info.java20
107 files changed, 15355 insertions, 0 deletions
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/app/impl/ApplicationManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/app/impl/ApplicationManager.java
new file mode 100644
index 00000000..161659f9
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/app/impl/ApplicationManager.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.app.impl;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.karaf.features.Feature;
+import org.apache.karaf.features.FeaturesService;
+import org.onosproject.app.ApplicationAdminService;
+import org.onosproject.app.ApplicationEvent;
+import org.onosproject.app.ApplicationListener;
+import org.onosproject.app.ApplicationService;
+import org.onosproject.app.ApplicationState;
+import org.onosproject.app.ApplicationStore;
+import org.onosproject.app.ApplicationStoreDelegate;
+import org.onosproject.event.AbstractListenerManager;
+import org.onosproject.core.Application;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.security.Permission;
+import org.onosproject.security.SecurityUtil;
+import org.slf4j.Logger;
+
+import java.io.InputStream;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.app.ApplicationEvent.Type.*;
+import static org.onosproject.security.AppPermission.Type.*;
+import static org.onosproject.security.AppGuard.checkPermission;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Implementation of the application management service.
+ */
+@Component(immediate = true)
+@Service
+public class ApplicationManager
+ extends AbstractListenerManager<ApplicationEvent, ApplicationListener>
+ implements ApplicationService, ApplicationAdminService {
+
+ private final Logger log = getLogger(getClass());
+
+ private static final String APP_ID_NULL = "Application ID cannot be null";
+
+ private final ApplicationStoreDelegate delegate = new InternalStoreDelegate();
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ApplicationStore store;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FeaturesService featuresService;
+
+ private boolean initializing;
+
+ @Activate
+ public void activate() {
+ eventDispatcher.addSink(ApplicationEvent.class, listenerRegistry);
+
+ initializing = true;
+ store.setDelegate(delegate);
+ initializing = false;
+
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ eventDispatcher.removeSink(ApplicationEvent.class);
+ store.unsetDelegate(delegate);
+ log.info("Stopped");
+ }
+
+ @Override
+ public Set<Application> getApplications() {
+ checkPermission(APP_READ);
+ return store.getApplications();
+ }
+
+ @Override
+ public ApplicationId getId(String name) {
+ checkPermission(APP_READ);
+ checkNotNull(name, "Name cannot be null");
+ return store.getId(name);
+ }
+
+ @Override
+ public Application getApplication(ApplicationId appId) {
+ checkPermission(APP_READ);
+ checkNotNull(appId, APP_ID_NULL);
+ return store.getApplication(appId);
+ }
+
+ @Override
+ public ApplicationState getState(ApplicationId appId) {
+ checkPermission(APP_READ);
+ checkNotNull(appId, APP_ID_NULL);
+ return store.getState(appId);
+ }
+
+ @Override
+ public Set<Permission> getPermissions(ApplicationId appId) {
+ checkPermission(APP_READ);
+ checkNotNull(appId, APP_ID_NULL);
+ return store.getPermissions(appId);
+ }
+
+ @Override
+ public Application install(InputStream appDescStream) {
+ checkNotNull(appDescStream, "Application archive stream cannot be null");
+ Application app = store.create(appDescStream);
+ SecurityUtil.register(app.id());
+ return app;
+ }
+
+ @Override
+ public void uninstall(ApplicationId appId) {
+ checkNotNull(appId, APP_ID_NULL);
+ try {
+ store.remove(appId);
+ } catch (Exception e) {
+ log.warn("Unable to purge application directory for {}", appId.name());
+ }
+ }
+
+ @Override
+ public void activate(ApplicationId appId) {
+ checkNotNull(appId, APP_ID_NULL);
+ if (!SecurityUtil.isAppSecured(appId)) {
+ return;
+ }
+ store.activate(appId);
+ }
+
+ @Override
+ public void deactivate(ApplicationId appId) {
+ checkNotNull(appId, APP_ID_NULL);
+ store.deactivate(appId);
+ }
+
+ @Override
+ public void setPermissions(ApplicationId appId, Set<Permission> permissions) {
+ checkNotNull(appId, APP_ID_NULL);
+ checkNotNull(permissions, "Permissions cannot be null");
+ store.setPermissions(appId, permissions);
+ }
+
+ private class InternalStoreDelegate implements ApplicationStoreDelegate {
+ @Override
+ public void notify(ApplicationEvent event) {
+ ApplicationEvent.Type type = event.type();
+ Application app = event.subject();
+ try {
+ if (type == APP_ACTIVATED) {
+ if (installAppFeatures(app)) {
+ log.info("Application {} has been activated", app.id().name());
+ }
+
+ } else if (type == APP_DEACTIVATED) {
+ if (uninstallAppFeatures(app)) {
+ log.info("Application {} has been deactivated", app.id().name());
+ }
+
+ } else if (type == APP_INSTALLED) {
+ if (installAppArtifacts(app)) {
+ log.info("Application {} has been installed", app.id().name());
+ }
+
+ } else if (type == APP_UNINSTALLED) {
+ if (uninstallAppFeatures(app) || uninstallAppArtifacts(app)) {
+ log.info("Application {} has been uninstalled", app.id().name());
+ }
+
+ }
+ post(event);
+
+ } catch (Exception e) {
+ log.warn("Unable to perform operation on application " + app.id().name(), e);
+ }
+ }
+ }
+
+ // The following methods are fully synchronized to guard against remote vs.
+ // locally induced feature service interactions.
+
+ private synchronized boolean installAppArtifacts(Application app) throws Exception {
+ if (app.featuresRepo().isPresent() &&
+ featuresService.getRepository(app.featuresRepo().get()) == null) {
+ featuresService.addRepository(app.featuresRepo().get());
+ return true;
+ }
+ return false;
+ }
+
+ private synchronized boolean uninstallAppArtifacts(Application app) throws Exception {
+ if (app.featuresRepo().isPresent() &&
+ featuresService.getRepository(app.featuresRepo().get()) != null) {
+ featuresService.removeRepository(app.featuresRepo().get());
+ return true;
+ }
+ return false;
+ }
+
+ private synchronized boolean installAppFeatures(Application app) throws Exception {
+ boolean changed = false;
+ for (String name : app.features()) {
+ Feature feature = featuresService.getFeature(name);
+ if (feature != null && !featuresService.isInstalled(feature)) {
+ featuresService.installFeature(name);
+ changed = true;
+ } else if (feature == null && !initializing) {
+ // Suppress feature-not-found reporting during startup since these
+ // can arise naturally from the staggered cluster install.
+ log.warn("Feature {} not found", name);
+ }
+ }
+ return changed;
+ }
+
+ private synchronized boolean uninstallAppFeatures(Application app) throws Exception {
+ boolean changed = false;
+ for (String name : app.features()) {
+ Feature feature = featuresService.getFeature(name);
+ if (feature != null && featuresService.isInstalled(feature)) {
+ featuresService.uninstallFeature(name);
+ changed = true;
+ } else if (feature == null) {
+ log.warn("Feature {} not found", name);
+ }
+ }
+ return changed;
+ }
+
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/app/impl/package-info.java b/framework/src/onos/core/net/src/main/java/org/onosproject/app/impl/package-info.java
new file mode 100644
index 00000000..d5f30374
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/app/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Subsystem for managing applications.
+ */
+package org.onosproject.app.impl; \ No newline at end of file
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/cfg/impl/ComponentConfigManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/cfg/impl/ComponentConfigManager.java
new file mode 100644
index 00000000..1933ee55
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/cfg/impl/ComponentConfigManager.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.cfg.impl;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.util.AbstractAccumulator;
+import org.onlab.util.Accumulator;
+import org.onlab.util.SharedExecutors;
+import org.onosproject.cfg.ComponentConfigEvent;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.cfg.ComponentConfigStore;
+import org.onosproject.cfg.ComponentConfigStoreDelegate;
+import org.onosproject.cfg.ConfigProperty;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.slf4j.Logger;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.security.AppGuard.checkPermission;
+import static org.slf4j.LoggerFactory.getLogger;
+import static org.onosproject.security.AppPermission.Type.*;
+
+
+/**
+ * Implementation of the centralized component configuration service.
+ */
+@Component(immediate = true)
+@Service
+public class ComponentConfigManager implements ComponentConfigService {
+
+ private static final String COMPONENT_NULL = "Component name cannot be null";
+ private static final String PROPERTY_NULL = "Property name cannot be null";
+
+ //Symbolic constants for use with the accumulator
+ private static final int MAX_ITEMS = 100;
+ private static final int MAX_BATCH_MILLIS = 1000;
+ private static final int MAX_IDLE_MILLIS = 250;
+
+ private static final String RESOURCE_EXT = ".cfgdef";
+
+ private final Logger log = getLogger(getClass());
+
+ private final ComponentConfigStoreDelegate delegate = new InternalStoreDelegate();
+ private final InternalAccumulator accumulator = new InternalAccumulator();
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ComponentConfigStore store;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ConfigurationAdmin cfgAdmin;
+
+ // Locally maintained catalog of definitions.
+ private final Map<String, Map<String, ConfigProperty>> properties =
+ Maps.newConcurrentMap();
+
+
+ @Activate
+ public void activate() {
+ store.setDelegate(delegate);
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ store.unsetDelegate(delegate);
+ log.info("Stopped");
+ }
+
+ @Override
+ public Set<String> getComponentNames() {
+ checkPermission(CONFIG_READ);
+
+ return ImmutableSet.copyOf(properties.keySet());
+ }
+
+ @Override
+ public void registerProperties(Class<?> componentClass) {
+ checkPermission(CONFIG_WRITE);
+
+ String componentName = componentClass.getName();
+ String resourceName = componentClass.getSimpleName() + RESOURCE_EXT;
+ try (InputStream ris = componentClass.getResourceAsStream(resourceName)) {
+ checkArgument(ris != null, "Property definitions not found at resource %s",
+ resourceName);
+
+ // Read the definitions
+ Set<ConfigProperty> defs = ConfigPropertyDefinitions.read(ris);
+
+ // Produce a new map of the properties and register it.
+ Map<String, ConfigProperty> map = Maps.newConcurrentMap();
+ defs.forEach(p -> map.put(p.name(), p));
+
+ properties.put(componentName, map);
+ loadExistingValues(componentName);
+ } catch (IOException e) {
+ log.error("Unable to read property definitions from resource " + resourceName, e);
+ }
+ }
+
+ @Override
+ public void unregisterProperties(Class<?> componentClass, boolean clear) {
+ checkPermission(CONFIG_WRITE);
+
+ String componentName = componentClass.getName();
+ checkNotNull(componentName, COMPONENT_NULL);
+ Map<String, ConfigProperty> cps = properties.remove(componentName);
+ if (clear && cps != null) {
+ cps.keySet().forEach(name -> store.unsetProperty(componentName, name));
+ clearExistingValues(componentName);
+ }
+ }
+
+ // Clears any existing values that may have been set.
+ private void clearExistingValues(String componentName) {
+ triggerUpdate(componentName);
+ }
+
+ @Override
+ public Set<ConfigProperty> getProperties(String componentName) {
+ checkPermission(CONFIG_READ);
+
+ Map<String, ConfigProperty> map = properties.get(componentName);
+ return map != null ? ImmutableSet.copyOf(map.values()) : null;
+ }
+
+ @Override
+ public void setProperty(String componentName, String name, String value) {
+ checkPermission(CONFIG_WRITE);
+
+ checkNotNull(componentName, COMPONENT_NULL);
+ checkNotNull(name, PROPERTY_NULL);
+ store.setProperty(componentName, name, value);
+ }
+
+ @Override
+ public void unsetProperty(String componentName, String name) {
+ checkPermission(CONFIG_WRITE);
+
+ checkNotNull(componentName, COMPONENT_NULL);
+ checkNotNull(name, PROPERTY_NULL);
+ store.unsetProperty(componentName, name);
+ }
+
+ private class InternalStoreDelegate implements ComponentConfigStoreDelegate {
+
+ @Override
+ public void notify(ComponentConfigEvent event) {
+ String componentName = event.subject();
+ String name = event.name();
+ String value = event.value();
+
+ switch (event.type()) {
+ case PROPERTY_SET:
+ set(componentName, name, value);
+ break;
+ case PROPERTY_UNSET:
+ reset(componentName, name);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ // Buffers multiple subsequent configuration updates into one notification.
+ private class InternalAccumulator extends AbstractAccumulator<String>
+ implements Accumulator<String> {
+
+ protected InternalAccumulator() {
+ super(SharedExecutors.getTimer(), MAX_ITEMS, MAX_BATCH_MILLIS, MAX_IDLE_MILLIS);
+ }
+
+ @Override
+ public void processItems(List<String> items) {
+ // Conversion to set removes duplicates
+ Set<String> componentSet = new HashSet<>(items);
+ componentSet.forEach(ComponentConfigManager.this::triggerUpdate);
+ }
+ }
+
+ // Locates the property in the component map and replaces it with an
+ // updated copy.
+ private void set(String componentName, String name, String value) {
+ Map<String, ConfigProperty> map = properties.get(componentName);
+ if (map != null) {
+ ConfigProperty prop = map.get(name);
+ if (prop != null) {
+ map.put(name, ConfigProperty.setProperty(prop, value));
+ accumulator.add(componentName);
+ return;
+ }
+ }
+ log.warn("Unable to set non-existent property {} for component {}",
+ name, componentName);
+ }
+
+ // Locates the property in the component map and replaces it with an
+ // reset copy.
+ private void reset(String componentName, String name) {
+ Map<String, ConfigProperty> map = properties.get(componentName);
+ if (map != null) {
+ ConfigProperty prop = map.get(name);
+ if (prop != null) {
+ map.put(name, ConfigProperty.resetProperty(prop));
+ accumulator.add(componentName);
+ return;
+ }
+ log.warn("Unable to reset non-existent property {} for component {}",
+ name, componentName);
+ }
+ }
+
+ // Loads existing property values that may have been set.
+ private void loadExistingValues(String componentName) {
+ try {
+ Configuration cfg = cfgAdmin.getConfiguration(componentName, null);
+ Map<String, ConfigProperty> map = properties.get(componentName);
+ Dictionary<String, Object> props = cfg.getProperties();
+ if (props != null) {
+ Enumeration<String> it = props.keys();
+ while (it.hasMoreElements()) {
+ String name = it.nextElement();
+ ConfigProperty p = map.get(name);
+ if (p != null) {
+ map.put(name, ConfigProperty.setProperty(p, (String) props.get(name)));
+ }
+ }
+ }
+ } catch (IOException e) {
+ log.error("Unable to get configuration for " + componentName, e);
+ }
+
+ }
+
+ // FIXME: This should be a slightly deferred execution to allow changing
+ // values just once per component when a number of updates arrive shortly
+ // after each other.
+ private void triggerUpdate(String componentName) {
+ try {
+ Configuration cfg = cfgAdmin.getConfiguration(componentName, null);
+ Map<String, ConfigProperty> map = properties.get(componentName);
+ Dictionary<String, Object> props = new Hashtable<>();
+ map.values().forEach(p -> props.put(p.name(), p.value()));
+ cfg.update(props);
+ } catch (IOException e) {
+ log.warn("Unable to update configuration for " + componentName, e);
+ }
+ }
+
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/cfg/impl/ConfigPropertyDefinitions.java b/framework/src/onos/core/net/src/main/java/org/onosproject/cfg/impl/ConfigPropertyDefinitions.java
new file mode 100644
index 00000000..0f416c76
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/cfg/impl/ConfigPropertyDefinitions.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.cfg.impl;
+
+import com.google.common.collect.ImmutableSet;
+import org.onosproject.cfg.ConfigProperty;
+import org.onosproject.cfg.ConfigProperty.Type;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.Set;
+
+import static org.onosproject.cfg.ConfigProperty.defineProperty;
+
+/**
+ * Utility for writing and reading configuration property definition file.
+ */
+public final class ConfigPropertyDefinitions {
+
+ private static final String FMT = "%s|%s|%s|%s\n";
+ private static final String SEP = "\\|";
+ private static final String COMMENT = "#";
+
+ private ConfigPropertyDefinitions() {
+ }
+
+ /**
+ * Writes the specified set of property definitions into the given output
+ * stream.
+ *
+ * @param stream output stream
+ * @param props properties whose definitions are to be written
+ * @throws java.io.IOException if unable to write the stream
+ */
+ public static void write(OutputStream stream, Set<ConfigProperty> props) throws IOException {
+ try (PrintWriter pw = new PrintWriter(new OutputStreamWriter(stream))) {
+ props.forEach(p -> pw.format(FMT, p.name(), p.type(), p.description(), p.defaultValue()));
+ }
+ }
+
+ /**
+ * Reads the specified input stream and creates from its contents a
+ * set of property definitions.
+ *
+ * @param stream input stream
+ * @return properties whose definitions are contained in the stream
+ * @throws java.io.IOException if unable to read the stream
+ */
+ public static Set<ConfigProperty> read(InputStream stream) throws IOException {
+ ImmutableSet.Builder<ConfigProperty> builder = ImmutableSet.builder();
+ try (BufferedReader br = new BufferedReader(new InputStreamReader(stream))) {
+ String line;
+ while ((line = br.readLine()) != null) {
+ if (!line.isEmpty() && !line.startsWith(COMMENT)) {
+ String[] f = line.split(SEP, 4);
+ builder.add(defineProperty(f[0], Type.valueOf(f[1]), f[2], f[3]));
+ }
+ }
+ }
+ return builder.build();
+ }
+
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/cfg/impl/package-info.java b/framework/src/onos/core/net/src/main/java/org/onosproject/cfg/impl/package-info.java
new file mode 100644
index 00000000..4f76c31c
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/cfg/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Subsystem for central management of component configurations.
+ */
+package org.onosproject.cfg.impl; \ No newline at end of file
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/cluster/impl/ClusterManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/cluster/impl/ClusterManager.java
new file mode 100644
index 00000000..04d1dfdf
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/cluster/impl/ClusterManager.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.cluster.impl;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.karaf.system.SystemService;
+import org.joda.time.DateTime;
+import org.onlab.packet.IpAddress;
+import org.onosproject.cluster.ClusterAdminService;
+import org.onosproject.cluster.ClusterDefinitionService;
+import org.onosproject.cluster.ClusterEvent;
+import org.onosproject.cluster.ClusterEventListener;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.ClusterStore;
+import org.onosproject.cluster.ClusterStoreDelegate;
+import org.onosproject.cluster.ControllerNode;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.event.AbstractListenerManager;
+import org.slf4j.Logger;
+
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.security.AppGuard.checkPermission;
+import static org.slf4j.LoggerFactory.getLogger;
+import static org.onosproject.security.AppPermission.Type.*;
+
+
+
+/**
+ * Implementation of the cluster service.
+ */
+@Component(immediate = true)
+@Service
+public class ClusterManager
+ extends AbstractListenerManager<ClusterEvent, ClusterEventListener>
+ implements ClusterService, ClusterAdminService {
+
+ public static final String INSTANCE_ID_NULL = "Instance ID cannot be null";
+ private final Logger log = getLogger(getClass());
+
+ private ClusterStoreDelegate delegate = new InternalStoreDelegate();
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ClusterDefinitionService clusterDefinitionService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ClusterStore store;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected SystemService systemService;
+
+ @Activate
+ public void activate() {
+ store.setDelegate(delegate);
+ eventDispatcher.addSink(ClusterEvent.class, listenerRegistry);
+ clusterDefinitionService.seedNodes()
+ .forEach(node -> store.addNode(node.id(), node.ip(), node.tcpPort()));
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ store.unsetDelegate(delegate);
+ eventDispatcher.removeSink(ClusterEvent.class);
+ log.info("Stopped");
+ }
+
+ @Override
+ public ControllerNode getLocalNode() {
+ checkPermission(CLUSTER_READ);
+ return store.getLocalNode();
+ }
+
+ @Override
+ public Set<ControllerNode> getNodes() {
+ checkPermission(CLUSTER_READ);
+ return store.getNodes();
+ }
+
+ @Override
+ public ControllerNode getNode(NodeId nodeId) {
+ checkPermission(CLUSTER_READ);
+ checkNotNull(nodeId, INSTANCE_ID_NULL);
+ return store.getNode(nodeId);
+ }
+
+ @Override
+ public ControllerNode.State getState(NodeId nodeId) {
+ checkPermission(CLUSTER_READ);
+ checkNotNull(nodeId, INSTANCE_ID_NULL);
+ return store.getState(nodeId);
+ }
+
+
+ @Override
+ public DateTime getLastUpdated(NodeId nodeId) {
+ checkPermission(CLUSTER_READ);
+ return store.getLastUpdated(nodeId);
+ }
+
+ @Override
+ public void formCluster(Set<ControllerNode> nodes, String ipPrefix) {
+ checkNotNull(nodes, "Nodes cannot be null");
+ checkArgument(!nodes.isEmpty(), "Nodes cannot be empty");
+ checkNotNull(ipPrefix, "IP prefix cannot be null");
+ clusterDefinitionService.formCluster(nodes, ipPrefix);
+ try {
+ log.warn("Shutting down container for cluster reconfiguration!");
+ systemService.reboot("now", SystemService.Swipe.NONE);
+ } catch (Exception e) {
+ log.error("Unable to reboot container", e);
+ }
+ }
+
+ @Override
+ public ControllerNode addNode(NodeId nodeId, IpAddress ip, int tcpPort) {
+ checkNotNull(nodeId, INSTANCE_ID_NULL);
+ checkNotNull(ip, "IP address cannot be null");
+ checkArgument(tcpPort > 5000, "TCP port must be > 5000");
+ return store.addNode(nodeId, ip, tcpPort);
+ }
+
+ @Override
+ public void removeNode(NodeId nodeId) {
+ checkNotNull(nodeId, INSTANCE_ID_NULL);
+ store.removeNode(nodeId);
+ }
+
+ // Store delegate to re-post events emitted from the store.
+ private class InternalStoreDelegate implements ClusterStoreDelegate {
+ @Override
+ public void notify(ClusterEvent event) {
+ post(event);
+ }
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/cluster/impl/MastershipManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/cluster/impl/MastershipManager.java
new file mode 100644
index 00000000..56d369fd
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/cluster/impl/MastershipManager.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.cluster.impl;
+
+import com.codahale.metrics.Timer;
+import com.codahale.metrics.Timer.Context;
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.Futures;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.metrics.MetricsService;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.ControllerNode;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.cluster.RoleInfo;
+import org.onosproject.event.AbstractListenerManager;
+import org.onosproject.core.MetricsHelper;
+import org.onosproject.mastership.MastershipAdminService;
+import org.onosproject.mastership.MastershipEvent;
+import org.onosproject.mastership.MastershipListener;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.mastership.MastershipStore;
+import org.onosproject.mastership.MastershipStoreDelegate;
+import org.onosproject.mastership.MastershipTerm;
+import org.onosproject.mastership.MastershipTermService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.MastershipRole;
+import org.slf4j.Logger;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Lists.newArrayList;
+import static org.onlab.metrics.MetricsUtil.startTimer;
+import static org.onlab.metrics.MetricsUtil.stopTimer;
+import static org.onosproject.cluster.ControllerNode.State.ACTIVE;
+import static org.onosproject.net.MastershipRole.MASTER;
+import static org.onosproject.security.AppGuard.checkPermission;
+import static org.slf4j.LoggerFactory.getLogger;
+import static org.onosproject.security.AppPermission.Type.*;
+
+
+
+@Component(immediate = true)
+@Service
+public class MastershipManager
+ extends AbstractListenerManager<MastershipEvent, MastershipListener>
+ implements MastershipService, MastershipAdminService, MastershipTermService,
+ MetricsHelper {
+
+ private static final String NODE_ID_NULL = "Node ID cannot be null";
+ private static final String DEVICE_ID_NULL = "Device ID cannot be null";
+ private static final String ROLE_NULL = "Mastership role cannot be null";
+
+ private final Logger log = getLogger(getClass());
+
+ private final MastershipStoreDelegate delegate = new InternalDelegate();
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected MastershipStore store;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ClusterService clusterService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected MetricsService metricsService;
+
+ private NodeId localNodeId;
+ private Timer requestRoleTimer;
+
+ @Activate
+ public void activate() {
+ requestRoleTimer = createTimer("Mastership", "requestRole", "responseTime");
+ localNodeId = clusterService.getLocalNode().id();
+ eventDispatcher.addSink(MastershipEvent.class, listenerRegistry);
+ store.setDelegate(delegate);
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ eventDispatcher.removeSink(MastershipEvent.class);
+ store.unsetDelegate(delegate);
+ log.info("Stopped");
+ }
+
+ @Override
+ public CompletableFuture<Void> setRole(NodeId nodeId, DeviceId deviceId, MastershipRole role) {
+ checkNotNull(nodeId, NODE_ID_NULL);
+ checkNotNull(deviceId, DEVICE_ID_NULL);
+ checkNotNull(role, ROLE_NULL);
+
+ CompletableFuture<MastershipEvent> eventFuture = null;
+
+ switch (role) {
+ case MASTER:
+ eventFuture = store.setMaster(nodeId, deviceId);
+ break;
+ case STANDBY:
+ eventFuture = store.setStandby(nodeId, deviceId);
+ break;
+ case NONE:
+ eventFuture = store.relinquishRole(nodeId, deviceId);
+ break;
+ default:
+ log.info("Unknown role; ignoring");
+ return CompletableFuture.completedFuture(null);
+ }
+
+ return eventFuture.thenAccept(this::post)
+ .thenApply(v -> null);
+ }
+
+ @Override
+ public MastershipRole getLocalRole(DeviceId deviceId) {
+ checkPermission(CLUSTER_READ);
+
+ checkNotNull(deviceId, DEVICE_ID_NULL);
+ return store.getRole(clusterService.getLocalNode().id(), deviceId);
+ }
+
+ @Override
+ public CompletableFuture<Void> relinquishMastership(DeviceId deviceId) {
+ checkPermission(CLUSTER_WRITE);
+ return store.relinquishRole(localNodeId, deviceId)
+ .thenAccept(this::post)
+ .thenApply(v -> null);
+ }
+
+ @Override
+ public CompletableFuture<MastershipRole> requestRoleFor(DeviceId deviceId) {
+ checkPermission(CLUSTER_WRITE);
+
+ checkNotNull(deviceId, DEVICE_ID_NULL);
+ final Context timer = startTimer(requestRoleTimer);
+ return store.requestRole(deviceId).whenComplete((result, error) -> stopTimer(timer));
+
+ }
+
+ @Override
+ public NodeId getMasterFor(DeviceId deviceId) {
+ checkPermission(CLUSTER_READ);
+
+ checkNotNull(deviceId, DEVICE_ID_NULL);
+ return store.getMaster(deviceId);
+ }
+
+ @Override
+ public Set<DeviceId> getDevicesOf(NodeId nodeId) {
+ checkPermission(CLUSTER_READ);
+
+ checkNotNull(nodeId, NODE_ID_NULL);
+ return store.getDevices(nodeId);
+ }
+
+ @Override
+ public RoleInfo getNodesFor(DeviceId deviceId) {
+ checkPermission(CLUSTER_READ);
+
+ checkNotNull(deviceId, DEVICE_ID_NULL);
+ return store.getNodes(deviceId);
+ }
+
+ @Override
+ public MastershipTerm getMastershipTerm(DeviceId deviceId) {
+ return store.getTermFor(deviceId);
+ }
+
+ @Override
+ public MetricsService metricsService() {
+ return metricsService;
+ }
+
+ @Override
+ public void balanceRoles() {
+ List<ControllerNode> nodes = newArrayList(clusterService.getNodes());
+ Map<ControllerNode, Set<DeviceId>> controllerDevices = new HashMap<>();
+ int deviceCount = 0;
+
+ // Create buckets reflecting current ownership.
+ for (ControllerNode node : nodes) {
+ if (clusterService.getState(node.id()) == ACTIVE) {
+ Set<DeviceId> devicesOf = new HashSet<>(getDevicesOf(node.id()));
+ deviceCount += devicesOf.size();
+ controllerDevices.put(node, devicesOf);
+ log.info("Node {} has {} devices.", node.id(), devicesOf.size());
+ }
+ }
+
+ // Now re-balance the buckets until they are roughly even.
+ List<CompletableFuture<Void>> balanceBucketsFutures = Lists.newLinkedList();
+ int rounds = controllerDevices.keySet().size();
+ for (int i = 0; i < rounds; i++) {
+ // Iterate over the buckets and find the smallest and the largest.
+ ControllerNode smallest = findBucket(true, controllerDevices);
+ ControllerNode largest = findBucket(false, controllerDevices);
+ balanceBucketsFutures.add(balanceBuckets(smallest, largest, controllerDevices, deviceCount));
+ }
+ CompletableFuture<Void> balanceRolesFuture = CompletableFuture.allOf(
+ balanceBucketsFutures.toArray(new CompletableFuture[balanceBucketsFutures.size()]));
+
+ Futures.getUnchecked(balanceRolesFuture);
+ }
+
+ private ControllerNode findBucket(boolean min,
+ Map<ControllerNode, Set<DeviceId>> controllerDevices) {
+ int xSize = min ? Integer.MAX_VALUE : -1;
+ ControllerNode xNode = null;
+ for (ControllerNode node : controllerDevices.keySet()) {
+ int size = controllerDevices.get(node).size();
+ if ((min && size < xSize) || (!min && size > xSize)) {
+ xSize = size;
+ xNode = node;
+ }
+ }
+ return xNode;
+ }
+
+ private CompletableFuture<Void> balanceBuckets(ControllerNode smallest, ControllerNode largest,
+ Map<ControllerNode, Set<DeviceId>> controllerDevices,
+ int deviceCount) {
+ Collection<DeviceId> minBucket = controllerDevices.get(smallest);
+ Collection<DeviceId> maxBucket = controllerDevices.get(largest);
+ int bucketCount = controllerDevices.keySet().size();
+
+ int delta = (maxBucket.size() - minBucket.size()) / 2;
+ delta = Math.min(deviceCount / bucketCount, delta);
+
+ List<CompletableFuture<Void>> setRoleFutures = Lists.newLinkedList();
+
+ if (delta > 0) {
+ log.info("Attempting to move {} nodes from {} to {}...", delta,
+ largest.id(), smallest.id());
+
+ int i = 0;
+ Iterator<DeviceId> it = maxBucket.iterator();
+ while (it.hasNext() && i < delta) {
+ DeviceId deviceId = it.next();
+ log.info("Setting {} as the master for {}", smallest.id(), deviceId);
+ setRoleFutures.add(setRole(smallest.id(), deviceId, MASTER));
+ controllerDevices.get(smallest).add(deviceId);
+ it.remove();
+ i++;
+ }
+ }
+
+ return CompletableFuture.allOf(setRoleFutures.toArray(new CompletableFuture[setRoleFutures.size()]));
+ }
+
+
+ public class InternalDelegate implements MastershipStoreDelegate {
+ @Override
+ public void notify(MastershipEvent event) {
+ post(event);
+ }
+ }
+
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/cluster/impl/package-info.java b/framework/src/onos/core/net/src/main/java/org/onosproject/cluster/impl/package-info.java
new file mode 100644
index 00000000..653edaa5
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/cluster/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Subsystem for tracking controller cluster nodes.
+ */
+package org.onosproject.cluster.impl;
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/core/impl/BlockAllocatorBasedIdGenerator.java b/framework/src/onos/core/net/src/main/java/org/onosproject/core/impl/BlockAllocatorBasedIdGenerator.java
new file mode 100644
index 00000000..267cd713
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/core/impl/BlockAllocatorBasedIdGenerator.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.core.impl;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.onosproject.core.IdBlock;
+import org.onosproject.core.IdGenerator;
+import org.onosproject.core.UnavailableIdException;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Base class of {@link IdGenerator} implementations which use {@link IdBlockAllocator} as
+ * backend.
+ */
+public class BlockAllocatorBasedIdGenerator implements IdGenerator {
+ protected final IdBlockAllocator allocator;
+ protected IdBlock idBlock;
+ protected AtomicBoolean initialized;
+
+
+ /**
+ * Constructs an ID generator which use {@link IdBlockAllocator} as backend.
+ *
+ * @param allocator the ID block allocator to use
+ */
+ protected BlockAllocatorBasedIdGenerator(IdBlockAllocator allocator) {
+ this.allocator = checkNotNull(allocator, "allocator cannot be null");
+ this.initialized = new AtomicBoolean(false);
+ }
+
+ @Override
+ public long getNewId() {
+ try {
+ if (!initialized.get()) {
+ synchronized (allocator) {
+ if (!initialized.get()) {
+ idBlock = allocator.allocateUniqueIdBlock();
+ initialized.set(true);
+ }
+ }
+ }
+ return idBlock.getNextId();
+ } catch (UnavailableIdException e) {
+ synchronized (allocator) {
+ idBlock = allocator.allocateUniqueIdBlock();
+ }
+ return idBlock.getNextId();
+ }
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/core/impl/CoreManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/core/impl/CoreManager.java
new file mode 100644
index 00000000..07612292
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/core/impl/CoreManager.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.core.impl;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Modified;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.util.SharedExecutors;
+import org.onlab.util.Tools;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.ApplicationIdStore;
+import org.onosproject.core.CoreService;
+import org.onosproject.core.IdBlockStore;
+import org.onosproject.core.IdGenerator;
+import org.onosproject.core.Version;
+import org.onosproject.event.EventDeliveryService;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.util.Dictionary;
+import java.util.List;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static org.onosproject.security.AppGuard.checkPermission;
+import static org.onosproject.security.AppPermission.Type.*;
+
+
+
+/**
+ * Core service implementation.
+ */
+@Component(immediate = true)
+@Service
+public class CoreManager implements CoreService {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private static final File VERSION_FILE = new File("../VERSION");
+ private static Version version = Version.version("1.3.0-SNAPSHOT");
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ApplicationIdStore applicationIdStore;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IdBlockStore idBlockStore;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ComponentConfigService cfgService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected EventDeliveryService eventDeliveryService;
+
+ private static final int DEFAULT_POOL_SIZE = 30;
+ @Property(name = "sharedThreadPoolSize", intValue = DEFAULT_POOL_SIZE,
+ label = "Configure shared pool maximum size ")
+ private int sharedThreadPoolSize = DEFAULT_POOL_SIZE;
+
+ private static final int DEFAULT_EVENT_TIME = 2000;
+ @Property(name = "maxEventTimeLimit", intValue = DEFAULT_EVENT_TIME,
+ label = "Maximum number of millis an event sink has to process an event")
+ private int maxEventTimeLimit = DEFAULT_EVENT_TIME;
+
+ @Activate
+ public void activate() {
+ registerApplication(CORE_APP_NAME);
+ cfgService.registerProperties(getClass());
+ List<String> versionLines = Tools.slurp(VERSION_FILE);
+ if (versionLines != null && !versionLines.isEmpty()) {
+ version = Version.version(versionLines.get(0));
+ }
+ }
+
+ @Deactivate
+ public void deactivate() {
+ cfgService.unregisterProperties(getClass(), false);
+ SharedExecutors.shutdown();
+ }
+
+ @Override
+ public Version version() {
+ checkPermission(APP_READ);
+
+ return version;
+ }
+
+ @Override
+ public Set<ApplicationId> getAppIds() {
+ checkPermission(APP_READ);
+
+ return applicationIdStore.getAppIds();
+ }
+
+ @Override
+ public ApplicationId getAppId(Short id) {
+ checkPermission(APP_READ);
+
+ return applicationIdStore.getAppId(id);
+ }
+
+ @Override
+ public ApplicationId getAppId(String name) {
+ checkPermission(APP_READ);
+
+ return applicationIdStore.getAppId(name);
+ }
+
+
+ @Override
+ public ApplicationId registerApplication(String name) {
+ checkNotNull(name, "Application ID cannot be null");
+ return applicationIdStore.registerApplication(name);
+ }
+
+ @Override
+ public IdGenerator getIdGenerator(String topic) {
+ IdBlockAllocator allocator = new StoreBasedIdBlockAllocator(topic, idBlockStore);
+ return new BlockAllocatorBasedIdGenerator(allocator);
+ }
+
+
+ @Modified
+ public void modified(ComponentContext context) {
+ Dictionary<?, ?> properties = context.getProperties();
+ Integer poolSize = getIntegerProperty(properties, "sharedThreadPoolSize");
+
+ if (poolSize != null && poolSize > 1) {
+ sharedThreadPoolSize = poolSize;
+ SharedExecutors.setPoolSize(sharedThreadPoolSize);
+ } else if (poolSize != null) {
+ log.warn("sharedThreadPoolSize must be greater than 1");
+ }
+
+ Integer timeLimit = getIntegerProperty(properties, "maxEventTimeLimit");
+ if (timeLimit != null && timeLimit > 1) {
+ maxEventTimeLimit = timeLimit;
+ eventDeliveryService.setDispatchTimeLimit(maxEventTimeLimit);
+ } else if (timeLimit != null) {
+ log.warn("maxEventTimeLimit must be greater than 1");
+ }
+
+ log.info("Settings: sharedThreadPoolSize={}, maxEventTimeLimit={}",
+ sharedThreadPoolSize, maxEventTimeLimit);
+ }
+
+
+ /**
+ * Get Integer property from the propertyName
+ * Return null if propertyName is not found.
+ *
+ * @param properties properties to be looked up
+ * @param propertyName the name of the property to look up
+ * @return value when the propertyName is defined or return null
+ */
+ private static Integer getIntegerProperty(Dictionary<?, ?> properties,
+ String propertyName) {
+ Integer value = null;
+ try {
+ String s = (String) properties.get(propertyName);
+ value = isNullOrEmpty(s) ? value : Integer.parseInt(s.trim());
+ } catch (NumberFormatException | ClassCastException e) {
+ value = null;
+ }
+ return value;
+ }
+
+
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/core/impl/IdBlockAllocator.java b/framework/src/onos/core/net/src/main/java/org/onosproject/core/impl/IdBlockAllocator.java
new file mode 100644
index 00000000..ba8594f2
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/core/impl/IdBlockAllocator.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.core.impl;
+
+import org.onosproject.core.IdBlock;
+
+/**
+ * An interface that gives unique ID spaces.
+ */
+public interface IdBlockAllocator {
+ /**
+ * Allocates a unique Id Block.
+ *
+ * @return Id Block.
+ */
+ IdBlock allocateUniqueIdBlock();
+
+ /**
+ * Allocates next unique id and retrieve a new range of ids if needed.
+ *
+ * @param range range to use for the identifier
+ * @return Id Block.
+ */
+ IdBlock allocateUniqueIdBlock(long range);
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/core/impl/MetricsManagerComponent.java b/framework/src/onos/core/net/src/main/java/org/onosproject/core/impl/MetricsManagerComponent.java
new file mode 100644
index 00000000..5a3f08df
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/core/impl/MetricsManagerComponent.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.core.impl;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Service;
+
+import org.onlab.metrics.MetricsManager;
+
+/**
+ * Metrics service implementation.
+ */
+@Component(immediate = true)
+@Service
+public class MetricsManagerComponent extends MetricsManager {
+
+ @Activate
+ protected void activate() {
+ super.clear();
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ super.clear();
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/core/impl/StoreBasedIdBlockAllocator.java b/framework/src/onos/core/net/src/main/java/org/onosproject/core/impl/StoreBasedIdBlockAllocator.java
new file mode 100644
index 00000000..c28de73d
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/core/impl/StoreBasedIdBlockAllocator.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.core.impl;
+
+import org.onosproject.core.IdBlock;
+import org.onosproject.core.IdBlockStore;
+
+public class StoreBasedIdBlockAllocator implements IdBlockAllocator {
+ private final IdBlockStore store;
+ private final String topic;
+
+ public StoreBasedIdBlockAllocator(String topic, IdBlockStore store) {
+ this.topic = topic;
+ this.store = store;
+ }
+
+ /**
+ * Returns a block of IDs which are unique and unused.
+ * Range of IDs is fixed size and is assigned incrementally as this method
+ * called.
+ *
+ * @return an IdBlock containing a set of unique IDs
+ */
+ @Override
+ public synchronized IdBlock allocateUniqueIdBlock() {
+ return store.getIdBlock(topic);
+ }
+
+ @Override
+ public IdBlock allocateUniqueIdBlock(long range) {
+ throw new UnsupportedOperationException("Not supported yet");
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/core/impl/package-info.java b/framework/src/onos/core/net/src/main/java/org/onosproject/core/impl/package-info.java
new file mode 100644
index 00000000..f8c3e672
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/core/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Miscellaneous core system implementations.
+ */
+package org.onosproject.core.impl;
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/event/impl/CoreEventDispatcher.java b/framework/src/onos/core/net/src/main/java/org/onosproject/event/impl/CoreEventDispatcher.java
new file mode 100644
index 00000000..79ce74b7
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/event/impl/CoreEventDispatcher.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.event.impl;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.util.SharedExecutors;
+import org.onosproject.event.AbstractEvent;
+import org.onosproject.event.DefaultEventSinkRegistry;
+import org.onosproject.event.Event;
+import org.onosproject.event.EventDeliveryService;
+import org.onosproject.event.EventSink;
+import org.slf4j.Logger;
+
+import java.util.TimerTask;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Simple implementation of an event dispatching service.
+ */
+@Component(immediate = true)
+@Service
+public class CoreEventDispatcher extends DefaultEventSinkRegistry
+ implements EventDeliveryService {
+
+ private final Logger log = getLogger(getClass());
+
+ // Default number of millis a sink can take to process an event.
+ private static final long DEFAULT_EXECUTE_MS = 5_000; // ms
+ private static final long WATCHDOG_MS = 250; // ms
+
+ private final BlockingQueue<Event> events = new LinkedBlockingQueue<>();
+
+ private final ExecutorService executor =
+ newSingleThreadExecutor(groupedThreads("onos/event", "dispatch-%d"));
+
+ @SuppressWarnings("unchecked")
+ private static final Event KILL_PILL = new AbstractEvent(null, 0) {
+ };
+
+ private DispatchLoop dispatchLoop;
+ private long maxProcessMillis = DEFAULT_EXECUTE_MS;
+
+ // Means to detect long-running sinks
+ private TimerTask watchdog;
+ private EventSink lastSink;
+ private long lastStart = 0;
+ private Future<?> dispatchFuture;
+
+ @Override
+ public void post(Event event) {
+ if (!events.add(event)) {
+ log.error("Unable to post event {}", event);
+ }
+ }
+
+ @Activate
+ public void activate() {
+ dispatchLoop = new DispatchLoop();
+ dispatchFuture = executor.submit(dispatchLoop);
+ watchdog = new Watchdog();
+ SharedExecutors.getTimer().schedule(watchdog, WATCHDOG_MS, WATCHDOG_MS);
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ dispatchLoop.stop();
+ watchdog.cancel();
+ post(KILL_PILL);
+ log.info("Stopped");
+ }
+
+ @Override
+ public void setDispatchTimeLimit(long millis) {
+ checkArgument(millis >= WATCHDOG_MS,
+ "Time limit must be greater than %s", WATCHDOG_MS);
+ maxProcessMillis = millis;
+ }
+
+ @Override
+ public long getDispatchTimeLimit() {
+ return maxProcessMillis;
+ }
+
+ // Auxiliary event dispatching loop that feeds off the events queue.
+ private class DispatchLoop implements Runnable {
+ private volatile boolean stopped;
+
+ @Override
+ public void run() {
+ stopped = false;
+ log.info("Dispatch loop initiated");
+ while (!stopped) {
+ try {
+ // Fetch the next event and if it is the kill-pill, bail
+ Event event = events.take();
+ if (event == KILL_PILL) {
+ break;
+ }
+ process(event);
+ } catch (InterruptedException e) {
+ log.warn("Dispatch loop interrupted");
+ } catch (Exception e) {
+ log.warn("Error encountered while dispatching event:", e);
+ }
+ }
+ log.info("Dispatch loop terminated");
+ }
+
+ // Locate the sink for the event class and use it to process the event
+ @SuppressWarnings("unchecked")
+ private void process(Event event) {
+ EventSink sink = getSink(event.getClass());
+ if (sink != null) {
+ lastSink = sink;
+ lastStart = System.currentTimeMillis();
+ sink.process(event);
+ lastStart = 0;
+ } else {
+ log.warn("No sink registered for event class {}",
+ event.getClass().getName());
+ }
+ }
+
+ void stop() {
+ stopped = true;
+ }
+ }
+
+ // Monitors event sinks to make sure none take too long to execute.
+ private class Watchdog extends TimerTask {
+ @Override
+ public void run() {
+ long delta = System.currentTimeMillis() - lastStart;
+ if (lastStart > 0 && delta > maxProcessMillis) {
+ lastStart = 0;
+ log.warn("Event sink {} exceeded execution time limit: {} ms; spawning new dispatch loop",
+ lastSink.getClass().getName(), delta);
+
+ // Notify the sink that it has exceeded its time limit.
+ lastSink.onProcessLimit();
+
+ // Cancel the old dispatch loop and submit a new one.
+ dispatchLoop.stop();
+ dispatchLoop = new DispatchLoop();
+ dispatchFuture.cancel(true);
+ dispatchFuture = executor.submit(dispatchLoop);
+ }
+ }
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/event/impl/package-info.java b/framework/src/onos/core/net/src/main/java/org/onosproject/event/impl/package-info.java
new file mode 100644
index 00000000..f0882f3d
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/event/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Local event dispatching mechanism.
+ */
+package org.onosproject.event.impl;
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/config/impl/BasicNetworkConfigs.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/config/impl/BasicNetworkConfigs.java
new file mode 100644
index 00000000..de2f5c3b
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/config/impl/BasicNetworkConfigs.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.config.impl;
+
+import com.google.common.collect.ImmutableSet;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onosproject.core.CoreService;
+import org.onosproject.incubator.net.config.basics.InterfaceConfig;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.HostId;
+import org.onosproject.net.LinkKey;
+import org.onosproject.net.config.ConfigFactory;
+import org.onosproject.net.config.NetworkConfigRegistry;
+import org.onosproject.net.config.basics.BasicDeviceConfig;
+import org.onosproject.net.config.basics.BasicHostConfig;
+import org.onosproject.net.config.basics.BasicLinkConfig;
+import org.onosproject.net.config.basics.OpticalPortConfig;
+import org.onosproject.net.config.basics.SubjectFactories;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Set;
+
+import static org.onosproject.net.config.basics.SubjectFactories.*;
+
+/**
+ * Component for registration of builtin basic network configurations.
+ */
+@Component(immediate = true)
+public class BasicNetworkConfigs {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private final Set<ConfigFactory> factories = ImmutableSet.of(
+ new ConfigFactory<DeviceId, BasicDeviceConfig>(DEVICE_SUBJECT_FACTORY,
+ BasicDeviceConfig.class,
+ "basic") {
+ @Override
+ public BasicDeviceConfig createConfig() {
+ return new BasicDeviceConfig();
+ }
+ },
+ new ConfigFactory<ConnectPoint, InterfaceConfig>(CONNECT_POINT_SUBJECT_FACTORY,
+ InterfaceConfig.class,
+ "interfaces",
+ true) {
+ @Override
+ public InterfaceConfig createConfig() {
+ return new InterfaceConfig();
+ }
+ },
+ new ConfigFactory<HostId, BasicHostConfig>(HOST_SUBJECT_FACTORY,
+ BasicHostConfig.class,
+ "basic") {
+ @Override
+ public BasicHostConfig createConfig() {
+ return new BasicHostConfig();
+ }
+ },
+ new ConfigFactory<LinkKey, BasicLinkConfig>(LINK_SUBJECT_FACTORY,
+ BasicLinkConfig.class,
+ "basic") {
+ @Override
+ public BasicLinkConfig createConfig() {
+ return new BasicLinkConfig();
+ }
+ },
+ new ConfigFactory<ConnectPoint, OpticalPortConfig>(CONNECT_POINT_SUBJECT_FACTORY,
+ OpticalPortConfig.class,
+ "optical") {
+ @Override
+ public OpticalPortConfig createConfig() {
+ return new OpticalPortConfig();
+ }
+ }
+ );
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected NetworkConfigRegistry registry;
+
+ @Activate
+ public void activate() {
+ SubjectFactories.setCoreService(coreService);
+ factories.forEach(registry::registerConfigFactory);
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ factories.forEach(registry::unregisterConfigFactory);
+ log.info("Stopped");
+ }
+
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/config/impl/NetworkConfigLoader.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/config/impl/NetworkConfigLoader.java
new file mode 100644
index 00000000..01348c15
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/config/impl/NetworkConfigLoader.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.config.impl;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.Maps;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onosproject.net.config.Config;
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.onosproject.net.config.NetworkConfigListener;
+import org.onosproject.net.config.NetworkConfigService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Component for loading the initial network configuration.
+ */
+@Component(immediate = true)
+public class NetworkConfigLoader {
+
+ private static final File CFG_FILE = new File("../config/network-cfg.json");
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected NetworkConfigService networkConfigService;
+
+ // FIXME: Add mutual exclusion to make sure this happens only once per startup.
+
+ private final Map<InnerConfigPosition, JsonNode> jsons = Maps.newConcurrentMap();
+
+ private final NetworkConfigListener configListener = new InnerConfigListener();
+
+ private ObjectNode root;
+
+ @Activate
+ public void activate() {
+ //TODO Maybe this should be at the bottom to avoid a potential race
+ networkConfigService.addListener(configListener);
+ try {
+ if (CFG_FILE.exists()) {
+ root = (ObjectNode) new ObjectMapper().readTree(CFG_FILE);
+
+ populateConfigurations();
+
+ applyConfigurations();
+
+ log.info("Loaded initial network configuration from {}", CFG_FILE);
+ }
+ } catch (Exception e) {
+ log.warn("Unable to load initial network configuration from {}",
+ CFG_FILE, e);
+ }
+ }
+
+ @Deactivate
+ public void deactivate() {
+ networkConfigService.removeListener(configListener);
+ }
+ // sweep through pending config jsons and try to add them
+
+ /**
+ * Inner class that allows for handling of newly added NetConfig types.
+ */
+ private final class InnerConfigListener implements NetworkConfigListener {
+
+ @Override
+ public void event(NetworkConfigEvent event) {
+ //TODO should this be done for other types of NetworkConfigEvents?
+ if (event.type() == NetworkConfigEvent.Type.CONFIG_REGISTERED ||
+ event.type() == NetworkConfigEvent.Type.CONFIG_ADDED) {
+ applyConfigurations();
+ }
+
+ }
+ }
+
+ /**
+ * Inner class that allows for tracking of JSON class configurations.
+ */
+ private final class InnerConfigPosition {
+ private final String subjectKey, subject, configKey;
+
+ private String subjectKey() {
+ return subjectKey;
+ }
+
+ private String subject() {
+ return subject;
+ }
+
+ private String configKey() {
+ return configKey;
+ }
+
+ private InnerConfigPosition(String subjectKey, String subject, String configKey) {
+ this.subjectKey = subjectKey;
+ this.subject = subject;
+ this.configKey = configKey;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof InnerConfigPosition) {
+ final InnerConfigPosition that = (InnerConfigPosition) obj;
+ return Objects.equals(this.subjectKey, that.subjectKey)
+ && Objects.equals(this.subject, that.subject)
+ && Objects.equals(this.configKey, that.configKey);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(subjectKey, subject, configKey);
+ }
+ }
+
+ /**
+ * Save the JSON leaves associated with a specific subject key.
+ *
+ * @param sk the subject key string.
+ * @param node the node associated with the subject key.
+ */
+ private void saveJson(String sk, ObjectNode node) {
+ node.fieldNames().forEachRemaining(s ->
+ saveSubjectJson(sk, s, (ObjectNode) node.path(s)));
+ }
+
+ /**
+ * Save the JSON leaves of the tree rooted as the node 'node' with subject key 'sk'.
+ *
+ * @param sk the string of the subject key.
+ * @param s the subject name.
+ * @param node the node rooting this subtree.
+ */
+ private void saveSubjectJson(String sk,
+ String s, ObjectNode node) {
+ node.fieldNames().forEachRemaining(c ->
+ this.jsons.put(new InnerConfigPosition(sk, s, c), node.path(c)));
+ }
+
+ /**
+ * Iterate through the JSON and populate a list of the leaf nodes of the structure.
+ */
+ private void populateConfigurations() {
+ root.fieldNames().forEachRemaining(sk ->
+ saveJson(sk, (ObjectNode) root.path(sk)));
+
+ }
+
+ /**
+ * Apply the configurations associated with all of the config classes that
+ * are imported and have not yet been applied.
+ */
+ private void applyConfigurations() {
+ Iterator<Map.Entry<InnerConfigPosition, JsonNode>> iter = jsons.entrySet().iterator();
+
+ Map.Entry<InnerConfigPosition, JsonNode> entry;
+ InnerConfigPosition key;
+ JsonNode node;
+ String subjectKey;
+ String subjectString;
+ String configKey;
+
+ while (iter.hasNext()) {
+ entry = iter.next();
+ node = entry.getValue();
+ key = entry.getKey();
+ subjectKey = key.subjectKey();
+ subjectString = key.subject();
+ configKey = key.configKey();
+
+ Class<? extends Config> configClass =
+ networkConfigService.getConfigClass(subjectKey, configKey);
+ //Check that the config class has been imported
+ if (configClass != null) {
+
+ Object subject = networkConfigService.getSubjectFactory(subjectKey).
+ createSubject(subjectString);
+
+ //Apply the configuration
+ networkConfigService.applyConfig(subject, configClass, node);
+
+ //Now that it has been applied the corresponding JSON entry is no longer needed
+ iter.remove();
+ }
+ }
+ }
+
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/config/impl/NetworkConfigManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/config/impl/NetworkConfigManager.java
new file mode 100644
index 00000000..5cd96cab
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/config/impl/NetworkConfigManager.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.config.impl;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.event.AbstractListenerManager;
+import org.onosproject.net.config.Config;
+import org.onosproject.net.config.ConfigFactory;
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.onosproject.net.config.NetworkConfigListener;
+import org.onosproject.net.config.NetworkConfigRegistry;
+import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.net.config.NetworkConfigStore;
+import org.onosproject.net.config.NetworkConfigStoreDelegate;
+import org.onosproject.net.config.SubjectFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Implementation of the network configuration subsystem.
+ */
+@Component(immediate = true)
+@Service
+public class NetworkConfigManager
+ extends AbstractListenerManager<NetworkConfigEvent, NetworkConfigListener>
+ implements NetworkConfigRegistry, NetworkConfigService {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private static final String NULL_FACTORY_MSG = "Factory cannot be null";
+ private static final String NULL_SCLASS_MSG = "Subject class cannot be null";
+ private static final String NULL_CCLASS_MSG = "Config class cannot be null";
+ private static final String NULL_SUBJECT_MSG = "Subject cannot be null";
+
+ // Inventory of configuration factories
+ private final Map<ConfigKey, ConfigFactory> factories = Maps.newConcurrentMap();
+
+ // Secondary indices to retrieve subject and config classes by keys
+ private final Map<String, SubjectFactory> subjectClasses = Maps.newConcurrentMap();
+ private final Map<Class, SubjectFactory> subjectClassKeys = Maps.newConcurrentMap();
+ private final Map<ConfigIdentifier, Class<? extends Config>> configClasses = Maps.newConcurrentMap();
+
+ private final NetworkConfigStoreDelegate storeDelegate = new InternalStoreDelegate();
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected NetworkConfigStore store;
+
+
+ @Activate
+ public void activate() {
+ eventDispatcher.addSink(NetworkConfigEvent.class, listenerRegistry);
+ store.setDelegate(storeDelegate);
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ eventDispatcher.removeSink(NetworkConfigEvent.class);
+ store.unsetDelegate(storeDelegate);
+ log.info("Stopped");
+ }
+
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void registerConfigFactory(ConfigFactory configFactory) {
+ checkNotNull(configFactory, NULL_FACTORY_MSG);
+ factories.put(key(configFactory), configFactory);
+ configClasses.put(identifier(configFactory), configFactory.configClass());
+
+ SubjectFactory subjectFactory = configFactory.subjectFactory();
+ subjectClasses.putIfAbsent(subjectFactory.subjectKey(), subjectFactory);
+ subjectClassKeys.putIfAbsent(subjectFactory.subjectClass(), subjectFactory);
+
+ store.addConfigFactory(configFactory);
+ }
+
+ @Override
+ public void unregisterConfigFactory(ConfigFactory configFactory) {
+ checkNotNull(configFactory, NULL_FACTORY_MSG);
+ factories.remove(key(configFactory));
+ configClasses.remove(identifier(configFactory));
+
+ // Note that we are deliberately not removing subject factory key bindings.
+ store.removeConfigFactory(configFactory);
+ }
+
+ @Override
+ public Set<ConfigFactory> getConfigFactories() {
+ return ImmutableSet.copyOf(factories.values());
+ }
+
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public <S, C extends Config<S>> Set<ConfigFactory<S, C>> getConfigFactories(Class<S> subjectClass) {
+ ImmutableSet.Builder<ConfigFactory<S, C>> builder = ImmutableSet.builder();
+ factories.forEach((key, factory) -> {
+ if (factory.subjectFactory().subjectClass().equals(subjectClass)) {
+ builder.add(factory);
+ }
+ });
+ return builder.build();
+ }
+
+ @Override
+ public <S, C extends Config<S>> ConfigFactory<S, C> getConfigFactory(Class<C> configClass) {
+ checkNotNull(configClass, NULL_CCLASS_MSG);
+ return store.getConfigFactory(configClass);
+ }
+
+
+ @Override
+ public Set<Class> getSubjectClasses() {
+ ImmutableSet.Builder<Class> builder = ImmutableSet.builder();
+ factories.forEach((k, v) -> builder.add(k.subjectClass));
+ return builder.build();
+ }
+
+ @Override
+ public SubjectFactory getSubjectFactory(String subjectKey) {
+ return subjectClasses.get(subjectKey);
+ }
+
+ @Override
+ public SubjectFactory getSubjectFactory(Class subjectClass) {
+ return subjectClassKeys.get(subjectClass);
+ }
+
+ @Override
+ public Class<? extends Config> getConfigClass(String subjectKey, String configKey) {
+ return configClasses.get(new ConfigIdentifier(subjectKey, configKey));
+ }
+
+ @Override
+ public <S> Set<S> getSubjects(Class<S> subjectClass) {
+ checkNotNull(subjectClass, NULL_SCLASS_MSG);
+ return store.getSubjects(subjectClass);
+ }
+
+ @Override
+ public <S, C extends Config<S>> Set<S> getSubjects(Class<S> subjectClass, Class<C> configClass) {
+ checkNotNull(subjectClass, NULL_SCLASS_MSG);
+ checkNotNull(configClass, NULL_CCLASS_MSG);
+ return store.getSubjects(subjectClass, configClass);
+ }
+
+ @Override
+ public <S> Set<Config<S>> getConfigs(S subject) {
+ checkNotNull(subject, NULL_SUBJECT_MSG);
+ Set<Class<? extends Config<S>>> configClasses = store.getConfigClasses(subject);
+ ImmutableSet.Builder<Config<S>> cfg = ImmutableSet.builder();
+ configClasses.forEach(cc -> cfg.add(store.getConfig(subject, cc)));
+ return cfg.build();
+ }
+
+ @Override
+ public <S, T extends Config<S>> T getConfig(S subject, Class<T> configClass) {
+ checkNotNull(subject, NULL_SUBJECT_MSG);
+ checkNotNull(configClass, NULL_CCLASS_MSG);
+ return store.getConfig(subject, configClass);
+ }
+
+
+ @Override
+ public <S, C extends Config<S>> C addConfig(S subject, Class<C> configClass) {
+ checkNotNull(subject, NULL_SUBJECT_MSG);
+ checkNotNull(configClass, NULL_CCLASS_MSG);
+ return store.createConfig(subject, configClass);
+ }
+
+ @Override
+ public <S, C extends Config<S>> C applyConfig(S subject, Class<C> configClass, JsonNode json) {
+ checkNotNull(subject, NULL_SUBJECT_MSG);
+ checkNotNull(configClass, NULL_CCLASS_MSG);
+ return store.applyConfig(subject, configClass, json);
+ }
+
+ @Override
+ public <S, C extends Config<S>> void removeConfig(S subject, Class<C> configClass) {
+ checkNotNull(subject, NULL_SUBJECT_MSG);
+ checkNotNull(configClass, NULL_CCLASS_MSG);
+ store.clearConfig(subject, configClass);
+ }
+
+ // Auxiliary store delegate to receive notification about changes in
+ // the network configuration store state - by the store itself.
+ private class InternalStoreDelegate implements NetworkConfigStoreDelegate {
+ @Override
+ public void notify(NetworkConfigEvent event) {
+ post(event);
+ }
+ }
+
+
+ // Produces a key for uniquely tracking a config factory.
+ private static ConfigKey key(ConfigFactory factory) {
+ return new ConfigKey(factory.subjectFactory().subjectClass(), factory.configClass());
+ }
+
+ // Auxiliary key to track config factories.
+ protected static final class ConfigKey {
+ final Class subjectClass;
+ final Class configClass;
+
+ protected ConfigKey(Class subjectClass, Class configClass) {
+ this.subjectClass = subjectClass;
+ this.configClass = configClass;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(subjectClass, configClass);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof ConfigKey) {
+ final ConfigKey other = (ConfigKey) obj;
+ return Objects.equals(this.subjectClass, other.subjectClass)
+ && Objects.equals(this.configClass, other.configClass);
+ }
+ return false;
+ }
+ }
+
+ private static ConfigIdentifier identifier(ConfigFactory factory) {
+ return new ConfigIdentifier(factory.subjectFactory().subjectKey(), factory.configKey());
+ }
+
+ static final class ConfigIdentifier {
+ final String subjectKey;
+ final String configKey;
+
+ protected ConfigIdentifier(String subjectKey, String configKey) {
+ this.subjectKey = subjectKey;
+ this.configKey = configKey;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(subjectKey, configKey);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof ConfigIdentifier) {
+ final ConfigIdentifier other = (ConfigIdentifier) obj;
+ return Objects.equals(this.subjectKey, other.subjectKey)
+ && Objects.equals(this.configKey, other.configKey);
+ }
+ return false;
+ }
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/config/impl/package-info.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/config/impl/package-info.java
new file mode 100644
index 00000000..38f76941
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/config/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Implementation of the network configuration subsystem.
+ */
+package org.onosproject.net.config.impl; \ No newline at end of file
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/BasicDeviceOperator.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/BasicDeviceOperator.java
new file mode 100644
index 00000000..7900d185
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/BasicDeviceOperator.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.device.impl;
+
+import static org.slf4j.LoggerFactory.getLogger;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.onosproject.net.config.ConfigOperator;
+import org.onosproject.net.config.basics.BasicDeviceConfig;
+import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.Device;
+import org.onosproject.net.SparseAnnotations;
+import org.onosproject.net.device.DefaultDeviceDescription;
+import org.onosproject.net.device.DeviceDescription;
+import org.slf4j.Logger;
+
+import java.util.Objects;
+
+/**
+ * Implementations of merge policies for various sources of device configuration
+ * information. This includes applications, provides, and network configurations.
+ */
+public final class BasicDeviceOperator implements ConfigOperator {
+
+ protected static final double DEFAULT_COORD = -1.0;
+ private static final Logger log = getLogger(BasicDeviceOperator.class);
+
+ private BasicDeviceOperator() {
+ }
+
+ /**
+ * Generates a DeviceDescription containing fields from a DeviceDescription and
+ * a DeviceConfig.
+ *
+ * @param bdc the device config entity from network config
+ * @param descr a DeviceDescription
+ * @return DeviceDescription based on both sources
+ */
+ public static DeviceDescription combine(BasicDeviceConfig bdc, DeviceDescription descr) {
+ if (bdc == null) {
+ return descr;
+ }
+
+ Device.Type type = descr.type();
+ if (bdc.type() != null && bdc.type() != type) {
+ type = bdc.type();
+ }
+
+ SparseAnnotations sa = combine(bdc, descr.annotations());
+ return new DefaultDeviceDescription(descr.deviceURI(), type, descr.manufacturer(),
+ descr.hwVersion(), descr.swVersion(),
+ descr.serialNumber(), descr.chassisId(), sa);
+ }
+
+ /**
+ * Generates an annotation from an existing annotation and DeviceConfig.
+ *
+ * @param bdc the device config entity from network config
+ * @param an the annotation
+ * @return annotation combining both sources
+ */
+ public static SparseAnnotations combine(BasicDeviceConfig bdc, SparseAnnotations an) {
+ DefaultAnnotations.Builder newBuilder = DefaultAnnotations.builder();
+ if (!Objects.equals(bdc.driver(), an.value(AnnotationKeys.DRIVER))) {
+ newBuilder.set(AnnotationKeys.DRIVER, bdc.driver());
+ }
+ if (bdc.name() != null) {
+ newBuilder.set(AnnotationKeys.NAME, bdc.name());
+ }
+ if (bdc.latitude() != DEFAULT_COORD) {
+ newBuilder.set(AnnotationKeys.LATITUDE, Double.toString(bdc.latitude()));
+ }
+ if (bdc.longitude() != DEFAULT_COORD) {
+ newBuilder.set(AnnotationKeys.LONGITUDE, Double.toString(bdc.longitude()));
+ }
+ if (bdc.rackAddress() != null) {
+ newBuilder.set(AnnotationKeys.RACK_ADDRESS, bdc.rackAddress());
+ }
+ if (bdc.owner() != null) {
+ newBuilder.set(AnnotationKeys.OWNER, bdc.owner());
+ }
+ DefaultAnnotations newAnnotations = newBuilder.build();
+ return DefaultAnnotations.union(an, newAnnotations);
+ }
+
+ public static DeviceDescription descriptionOf(Device device) {
+ checkNotNull(device, "Must supply non-null Device");
+ return new DefaultDeviceDescription(device.id().uri(), device.type(),
+ device.manufacturer(), device.hwVersion(),
+ device.swVersion(), device.serialNumber(),
+ device.chassisId(), (SparseAnnotations) device.annotations());
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/DeviceManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/DeviceManager.java
new file mode 100644
index 00000000..b0b3abe2
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/DeviceManager.java
@@ -0,0 +1,765 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.device.impl;
+
+import com.google.common.collect.Lists;
+import com.google.common.util.concurrent.Futures;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.net.provider.AbstractListenerProviderRegistry;
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.onosproject.net.config.NetworkConfigListener;
+import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.net.config.basics.BasicDeviceConfig;
+import org.onosproject.net.config.basics.OpticalPortConfig;
+import org.onosproject.mastership.MastershipEvent;
+import org.onosproject.mastership.MastershipListener;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.mastership.MastershipTerm;
+import org.onosproject.mastership.MastershipTermService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Device;
+import org.onosproject.net.Device.Type;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.MastershipRole;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DefaultDeviceDescription;
+import org.onosproject.net.device.DefaultPortDescription;
+import org.onosproject.net.device.DeviceAdminService;
+import org.onosproject.net.device.DeviceDescription;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.device.DeviceProvider;
+import org.onosproject.net.device.DeviceProviderRegistry;
+import org.onosproject.net.device.DeviceProviderService;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.device.DeviceStore;
+import org.onosproject.net.device.DeviceStoreDelegate;
+import org.onosproject.net.device.PortDescription;
+import org.onosproject.net.device.PortStatistics;
+import org.onosproject.net.provider.AbstractProviderService;
+import org.slf4j.Logger;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.net.MastershipRole.*;
+import static org.onosproject.security.AppGuard.checkPermission;
+import static org.slf4j.LoggerFactory.getLogger;
+import static org.onosproject.security.AppPermission.Type.*;
+
+/**
+ * Provides implementation of the device SB &amp; NB APIs.
+ */
+@Component(immediate = true)
+@Service
+public class DeviceManager
+ extends AbstractListenerProviderRegistry<DeviceEvent, DeviceListener, DeviceProvider, DeviceProviderService>
+ implements DeviceService, DeviceAdminService, DeviceProviderRegistry {
+
+ private static final String DEVICE_ID_NULL = "Device ID cannot be null";
+ private static final String PORT_NUMBER_NULL = "Port number cannot be null";
+ private static final String DEVICE_DESCRIPTION_NULL = "Device description cannot be null";
+ private static final String PORT_DESCRIPTION_NULL = "Port description cannot be null";
+ private static final String PORT_DESC_LIST_NULL = "Port description list cannot be null";
+
+ private final Logger log = getLogger(getClass());
+
+ private final DeviceStoreDelegate delegate = new InternalStoreDelegate();
+
+ private final MastershipListener mastershipListener = new InternalMastershipListener();
+ private NodeId localNodeId;
+
+ private ScheduledExecutorService backgroundService;
+
+ private final NetworkConfigListener networkConfigListener = new InternalNetworkConfigListener();
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceStore store;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ClusterService clusterService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected MastershipService mastershipService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected MastershipTermService termService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected NetworkConfigService networkConfigService;
+
+ @Activate
+ public void activate() {
+ backgroundService = newSingleThreadScheduledExecutor(groupedThreads("onos/device", "manager-background"));
+ localNodeId = clusterService.getLocalNode().id();
+
+ store.setDelegate(delegate);
+ eventDispatcher.addSink(DeviceEvent.class, listenerRegistry);
+ mastershipService.addListener(mastershipListener);
+ networkConfigService.addListener(networkConfigListener);
+
+ backgroundService.scheduleWithFixedDelay(() -> {
+ try {
+ mastershipCheck();
+ } catch (Exception e) {
+ log.error("Exception thrown during integrity check", e);
+ }
+ }, 1, 1, TimeUnit.MINUTES);
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ backgroundService.shutdown();
+ networkConfigService.removeListener(networkConfigListener);
+ store.unsetDelegate(delegate);
+ mastershipService.removeListener(mastershipListener);
+ eventDispatcher.removeSink(DeviceEvent.class);
+ log.info("Stopped");
+ }
+
+ @Override
+ public int getDeviceCount() {
+ checkPermission(DEVICE_READ);
+ return store.getDeviceCount();
+ }
+
+ @Override
+ public Iterable<Device> getDevices() {
+ checkPermission(DEVICE_READ);
+ return store.getDevices();
+ }
+
+ @Override
+ public Iterable<Device> getAvailableDevices() {
+ checkPermission(DEVICE_READ);
+ return store.getAvailableDevices();
+ }
+
+ @Override
+ public Device getDevice(DeviceId deviceId) {
+ checkPermission(DEVICE_READ);
+ checkNotNull(deviceId, DEVICE_ID_NULL);
+ return store.getDevice(deviceId);
+ }
+
+ @Override
+ public MastershipRole getRole(DeviceId deviceId) {
+ checkPermission(DEVICE_READ);
+ checkNotNull(deviceId, DEVICE_ID_NULL);
+ return mastershipService.getLocalRole(deviceId);
+ }
+
+ @Override
+ public List<Port> getPorts(DeviceId deviceId) {
+ checkPermission(DEVICE_READ);
+ checkNotNull(deviceId, DEVICE_ID_NULL);
+ return store.getPorts(deviceId);
+ }
+
+ @Override
+ public List<PortStatistics> getPortStatistics(DeviceId deviceId) {
+ checkPermission(DEVICE_READ);
+ checkNotNull(deviceId, DEVICE_ID_NULL);
+ return store.getPortStatistics(deviceId);
+ }
+
+ @Override
+ public List<PortStatistics> getPortDeltaStatistics(DeviceId deviceId) {
+ checkPermission(DEVICE_READ);
+ checkNotNull(deviceId, DEVICE_ID_NULL);
+ return store.getPortDeltaStatistics(deviceId);
+ }
+
+ @Override
+ public Port getPort(DeviceId deviceId, PortNumber portNumber) {
+ checkPermission(DEVICE_READ);
+ checkNotNull(deviceId, DEVICE_ID_NULL);
+ checkNotNull(portNumber, PORT_NUMBER_NULL);
+ return store.getPort(deviceId, portNumber);
+ }
+
+ @Override
+ public boolean isAvailable(DeviceId deviceId) {
+ checkPermission(DEVICE_READ);
+
+ checkNotNull(deviceId, DEVICE_ID_NULL);
+ return store.isAvailable(deviceId);
+ }
+
+ // Check a device for control channel connectivity.
+ private boolean isReachable(DeviceId deviceId) {
+ if (deviceId == null) {
+ return false;
+ }
+ DeviceProvider provider = getProvider(deviceId);
+ if (provider != null) {
+ return provider.isReachable(deviceId);
+ } else {
+ log.debug("Provider not found for {}", deviceId);
+ return false;
+ }
+ }
+
+ @Override
+ public void removeDevice(DeviceId deviceId) {
+ checkNotNull(deviceId, DEVICE_ID_NULL);
+ DeviceEvent event = store.removeDevice(deviceId);
+ if (event != null) {
+ log.info("Device {} administratively removed", deviceId);
+ post(event);
+ }
+ }
+
+ @Override
+ protected DeviceProviderService createProviderService(
+ DeviceProvider provider) {
+ return new InternalDeviceProviderService(provider);
+ }
+
+ /**
+ * Checks if all the reachable devices have a valid mastership role.
+ */
+ private void mastershipCheck() {
+ log.debug("Checking mastership");
+ for (Device device : getDevices()) {
+ final DeviceId deviceId = device.id();
+ log.trace("Checking device {}", deviceId);
+
+ if (!isReachable(deviceId)) {
+ continue;
+ }
+
+ if (mastershipService.getLocalRole(deviceId) != NONE) {
+ continue;
+ }
+
+ log.info("{} is reachable but did not have a valid role, reasserting", deviceId);
+
+ // isReachable but was not MASTER or STANDBY, get a role and apply
+ // Note: NONE triggers request to MastershipService
+ reassertRole(deviceId, NONE);
+ }
+ }
+
+ // Personalized device provider service issued to the supplied provider.
+ private class InternalDeviceProviderService
+ extends AbstractProviderService<DeviceProvider>
+ implements DeviceProviderService {
+
+ InternalDeviceProviderService(DeviceProvider provider) {
+ super(provider);
+ }
+
+ /**
+ * Apply role in reaction to provider event.
+ *
+ * @param deviceId device identifier
+ * @param newRole new role to apply to the device
+ * @return true if the request was sent to provider
+ */
+ private boolean applyRole(DeviceId deviceId, MastershipRole newRole) {
+
+ if (newRole.equals(MastershipRole.NONE)) {
+ //no-op
+ return true;
+ }
+
+ DeviceProvider provider = provider();
+ if (provider == null) {
+ log.warn("Provider for {} was not found. Cannot apply role {}",
+ deviceId, newRole);
+ return false;
+ }
+ provider.roleChanged(deviceId, newRole);
+ // not triggering probe when triggered by provider service event
+
+ return true;
+ }
+
+ @Override
+ public void deviceConnected(DeviceId deviceId,
+ DeviceDescription deviceDescription) {
+ checkNotNull(deviceId, DEVICE_ID_NULL);
+ checkNotNull(deviceDescription, DEVICE_DESCRIPTION_NULL);
+ checkValidity();
+
+ BasicDeviceConfig cfg = networkConfigService.getConfig(deviceId, BasicDeviceConfig.class);
+ if (!isAllowed(cfg)) {
+ log.warn("Device {} is not allowed", deviceId);
+ return;
+ }
+ // Generate updated description and establish my Role
+ deviceDescription = BasicDeviceOperator.combine(cfg, deviceDescription);
+ Futures.getUnchecked(mastershipService.requestRoleFor(deviceId)
+ .thenAccept(role -> {
+ log.info("Local role is {} for {}", role, deviceId);
+ applyRole(deviceId, role);
+ }));
+
+ DeviceEvent event = store.createOrUpdateDevice(provider().id(), deviceId,
+ deviceDescription);
+ log.info("Device {} connected", deviceId);
+ if (event != null) {
+ log.trace("event: {} {}", event.type(), event);
+ post(event);
+ }
+ }
+
+ @Override
+ public void deviceDisconnected(DeviceId deviceId) {
+ checkNotNull(deviceId, DEVICE_ID_NULL);
+ checkValidity();
+
+ log.info("Device {} disconnected from this node", deviceId);
+
+ List<Port> ports = store.getPorts(deviceId);
+ List<PortDescription> descs = Lists.newArrayList();
+ ports.forEach(port ->
+ descs.add(new DefaultPortDescription(port.number(),
+ false, port.type(),
+ port.portSpeed())));
+ store.updatePorts(this.provider().id(), deviceId, descs);
+ try {
+ if (mastershipService.isLocalMaster(deviceId)) {
+ post(store.markOffline(deviceId));
+ }
+ } catch (IllegalStateException e) {
+ log.warn("Failed to mark {} offline", deviceId);
+ // only the MASTER should be marking off-line in normal cases,
+ // but if I was the last STANDBY connection, etc. and no one else
+ // was there to mark the device offline, this instance may need to
+ // temporarily request for Master Role and mark offline.
+
+ //there are times when this node will correctly have mastership, BUT
+ //that isn't reflected in the ClockManager before the device disconnects.
+ //we want to let go of the device anyways, so make sure this happens.
+
+ // FIXME: Store semantics leaking out as IllegalStateException.
+ // Consider revising store API to handle this scenario.
+ CompletableFuture<MastershipRole> roleFuture = mastershipService.requestRoleFor(deviceId);
+ roleFuture.whenComplete((role, error) -> {
+ MastershipTerm term = termService.getMastershipTerm(deviceId);
+ // TODO: Move this type of check inside device clock manager, etc.
+ if (term != null && localNodeId.equals(term.master())) {
+ log.info("Retry marking {} offline", deviceId);
+ post(store.markOffline(deviceId));
+ } else {
+ log.info("Failed again marking {} offline. {}", deviceId, role);
+ }
+ });
+ } finally {
+ try {
+ //relinquish master role and ability to be backup.
+ mastershipService.relinquishMastership(deviceId).get();
+ } catch (InterruptedException e) {
+ log.warn("Interrupted while reliquishing role for {}", deviceId);
+ Thread.currentThread().interrupt();
+ } catch (ExecutionException e) {
+ log.error("Exception thrown while relinquishing role for {}", deviceId, e);
+ }
+ }
+ }
+
+ @Override
+ public void updatePorts(DeviceId deviceId,
+ List<PortDescription> portDescriptions) {
+ checkNotNull(deviceId, DEVICE_ID_NULL);
+ checkNotNull(portDescriptions, PORT_DESC_LIST_NULL);
+ checkValidity();
+ if (!mastershipService.isLocalMaster(deviceId)) {
+ // Never been a master for this device
+ // any update will be ignored.
+ log.trace("Ignoring {} port updates on standby node. {}", deviceId, portDescriptions);
+ return;
+ }
+ portDescriptions = portDescriptions.stream()
+ .map(e -> consolidate(deviceId, e))
+ .collect(Collectors.toList());
+ List<DeviceEvent> events = store.updatePorts(this.provider().id(),
+ deviceId, portDescriptions);
+ for (DeviceEvent event : events) {
+ post(event);
+ }
+ }
+
+ @Override
+ public void portStatusChanged(DeviceId deviceId,
+ PortDescription portDescription) {
+ checkNotNull(deviceId, DEVICE_ID_NULL);
+ checkNotNull(portDescription, PORT_DESCRIPTION_NULL);
+ checkValidity();
+
+ if (!mastershipService.isLocalMaster(deviceId)) {
+ // Never been a master for this device
+ // any update will be ignored.
+ log.trace("Ignoring {} port update on standby node. {}", deviceId,
+ portDescription);
+ return;
+ }
+ portDescription = consolidate(deviceId, portDescription);
+ final DeviceEvent event = store.updatePortStatus(this.provider().id(),
+ deviceId, portDescription);
+ if (event != null) {
+ log.info("Device {} port {} status changed", deviceId, event.port().number());
+ post(event);
+ }
+ }
+
+ // merges the appropriate PortConfig with the description.
+ private PortDescription consolidate(DeviceId did, PortDescription desc) {
+ switch (desc.type()) {
+ case COPPER:
+ case VIRTUAL:
+ return desc;
+ default:
+ OpticalPortConfig opc = networkConfigService.getConfig(
+ new ConnectPoint(did, desc.portNumber()), OpticalPortConfig.class);
+ return OpticalPortOperator.combine(opc, desc);
+ }
+ }
+
+ @Override
+ public void receivedRoleReply(DeviceId deviceId, MastershipRole requested,
+ MastershipRole response) {
+ // Several things can happen here:
+ // 1. request and response match
+ // 2. request and response don't match
+ // 3. MastershipRole and requested match (and 1 or 2 are true)
+ // 4. MastershipRole and requested don't match (and 1 or 2 are true)
+ //
+ // 2, 4, and 3 with case 2 are failure modes.
+
+ // FIXME: implement response to this notification
+
+ log.debug("got reply to a role request for {}: asked for {}, and got {}",
+ deviceId, requested, response);
+
+ if (requested == null && response == null) {
+ // something was off with DeviceProvider, maybe check channel too?
+ log.warn("Failed to assert role [{}] onto Device {}", requested, deviceId);
+ mastershipService.relinquishMastership(deviceId);
+ return;
+ }
+
+ if (Objects.equals(requested, response)) {
+ if (Objects.equals(requested, mastershipService.getLocalRole(deviceId))) {
+ return;
+ } else {
+ return;
+ // FIXME roleManager got the device to comply, but doesn't agree with
+ // the store; use the store's view, then try to reassert.
+ }
+ } else {
+ // we didn't get back what we asked for. Reelect someone else.
+ log.warn("Failed to assert role [{}] onto Device {}", response, deviceId);
+ if (response == MastershipRole.MASTER) {
+ mastershipService.relinquishMastership(deviceId);
+ // TODO: Shouldn't we be triggering event?
+ //final Device device = getDevice(deviceId);
+ //post(new DeviceEvent(DEVICE_MASTERSHIP_CHANGED, device));
+ }
+ }
+ }
+
+ @Override
+ public void updatePortStatistics(DeviceId deviceId, Collection<PortStatistics> portStatistics) {
+ checkNotNull(deviceId, DEVICE_ID_NULL);
+ checkNotNull(portStatistics, "Port statistics list cannot be null");
+ checkValidity();
+
+ DeviceEvent event = store.updatePortStatistics(this.provider().id(),
+ deviceId, portStatistics);
+ post(event);
+ }
+ }
+
+ // by default allowed, otherwise check flag
+ private boolean isAllowed(BasicDeviceConfig cfg) {
+ return (cfg == null || cfg.isAllowed());
+ }
+
+ // Applies the specified role to the device; ignores NONE
+
+ /**
+ * Apply role to device and send probe if MASTER.
+ *
+ * @param deviceId device identifier
+ * @param newRole new role to apply to the device
+ * @return true if the request was sent to provider
+ */
+ private boolean applyRoleAndProbe(DeviceId deviceId, MastershipRole newRole) {
+ if (newRole.equals(MastershipRole.NONE)) {
+ //no-op
+ return true;
+ }
+
+ DeviceProvider provider = getProvider(deviceId);
+ if (provider == null) {
+ log.warn("Provider for {} was not found. Cannot apply role {}", deviceId, newRole);
+ return false;
+ }
+ provider.roleChanged(deviceId, newRole);
+
+ if (newRole.equals(MastershipRole.MASTER)) {
+ // only trigger event when request was sent to provider
+ provider.triggerProbe(deviceId);
+ }
+ return true;
+ }
+
+ /**
+ * Reaasert role for specified device connected to this node.
+ *
+ * @param did device identifier
+ * @param nextRole role to apply. If NONE is specified,
+ * it will ask mastership service for a role and apply it.
+ */
+ private void reassertRole(final DeviceId did,
+ final MastershipRole nextRole) {
+
+ MastershipRole myNextRole = nextRole;
+ if (myNextRole == NONE) {
+ mastershipService.requestRoleFor(did);
+ MastershipTerm term = termService.getMastershipTerm(did);
+ if (term != null && localNodeId.equals(term.master())) {
+ myNextRole = MASTER;
+ } else {
+ myNextRole = STANDBY;
+ }
+ }
+
+ switch (myNextRole) {
+ case MASTER:
+ final Device device = getDevice(did);
+ if ((device != null) && !isAvailable(did)) {
+ //flag the device as online. Is there a better way to do this?
+ DefaultDeviceDescription deviceDescription
+ = new DefaultDeviceDescription(did.uri(),
+ device.type(),
+ device.manufacturer(),
+ device.hwVersion(),
+ device.swVersion(),
+ device.serialNumber(),
+ device.chassisId());
+ DeviceEvent devEvent =
+ store.createOrUpdateDevice(device.providerId(), did,
+ deviceDescription);
+ post(devEvent);
+ }
+ // TODO: should apply role only if there is mismatch
+ log.debug("Applying role {} to {}", myNextRole, did);
+ if (!applyRoleAndProbe(did, MASTER)) {
+ log.warn("Unsuccessful applying role {} to {}", myNextRole, did);
+ // immediately failed to apply role
+ mastershipService.relinquishMastership(did);
+ // FIXME disconnect?
+ }
+ break;
+ case STANDBY:
+ log.debug("Applying role {} to {}", myNextRole, did);
+ if (!applyRoleAndProbe(did, STANDBY)) {
+ log.warn("Unsuccessful applying role {} to {}", myNextRole, did);
+ // immediately failed to apply role
+ mastershipService.relinquishMastership(did);
+ // FIXME disconnect?
+ }
+ break;
+ case NONE:
+ default:
+ // should never reach here
+ log.error("You didn't see anything. I did not exist.");
+ break;
+ }
+ }
+
+ private void handleMastershipEvent(MastershipEvent event) {
+ if (event.type() != MastershipEvent.Type.MASTER_CHANGED) {
+ // Don't care if backup list changed.
+ return;
+ }
+
+ final DeviceId did = event.subject();
+
+ // myRole suggested by MastershipService
+ MastershipRole myNextRole;
+ if (localNodeId.equals(event.roleInfo().master())) {
+ // confirm latest info
+ MastershipTerm term = termService.getMastershipTerm(did);
+ final boolean iHaveControl = term != null && localNodeId.equals(term.master());
+ if (iHaveControl) {
+ myNextRole = MASTER;
+ } else {
+ myNextRole = STANDBY;
+ }
+ } else if (event.roleInfo().backups().contains(localNodeId)) {
+ myNextRole = STANDBY;
+ } else {
+ myNextRole = NONE;
+ }
+
+ final boolean isReachable = isReachable(did);
+ if (!isReachable) {
+ // device is not connected to this node
+ if (myNextRole != NONE) {
+ log.warn("Node was instructed to be {} role for {}, "
+ + "but this node cannot reach the device. "
+ + "Relinquishing role. ",
+ myNextRole, did);
+ mastershipService.relinquishMastership(did);
+ }
+ return;
+ }
+
+ // device is connected to this node:
+ if (store.getDevice(did) != null) {
+ reassertRole(did, myNextRole);
+ } else {
+ log.debug("Device is not yet/no longer in the store: {}", did);
+ }
+ }
+
+ // Intercepts mastership events
+ private class InternalMastershipListener implements MastershipListener {
+
+ @Override
+ public void event(MastershipEvent event) {
+ backgroundService.submit(() -> {
+ try {
+ handleMastershipEvent(event);
+ } catch (Exception e) {
+ log.warn("Failed to handle {}", event, e);
+ }
+ });
+ }
+ }
+
+ // Store delegate to re-post events emitted from the store.
+ private class InternalStoreDelegate implements DeviceStoreDelegate {
+ @Override
+ public void notify(DeviceEvent event) {
+ post(event);
+ }
+ }
+
+ @Override
+ public Iterable<Device> getDevices(Type type) {
+ checkPermission(DEVICE_READ);
+ Set<Device> results = new HashSet<>();
+ Iterable<Device> devices = store.getDevices();
+ if (devices != null) {
+ devices.forEach(d -> {
+ if (type.equals(d.type())) {
+ results.add(d);
+ }
+ });
+ }
+ return results;
+ }
+
+ @Override
+ public Iterable<Device> getAvailableDevices(Type type) {
+ checkPermission(DEVICE_READ);
+ Set<Device> results = new HashSet<>();
+ Iterable<Device> availableDevices = store.getAvailableDevices();
+ if (availableDevices != null) {
+ availableDevices.forEach(d -> {
+ if (type.equals(d.type())) {
+ results.add(d);
+ }
+ });
+ }
+ return results;
+ }
+
+ private class InternalNetworkConfigListener implements NetworkConfigListener {
+ @Override
+ public boolean isRelevant(NetworkConfigEvent event) {
+ return (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED
+ || event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED)
+ && (event.configClass().equals(BasicDeviceConfig.class)
+ || event.configClass().equals(OpticalPortConfig.class));
+ }
+
+ @Override
+ public void event(NetworkConfigEvent event) {
+ DeviceEvent de = null;
+ if (event.configClass().equals(BasicDeviceConfig.class)) {
+ log.info("Detected Device network config event {}", event.type());
+ DeviceId did = (DeviceId) event.subject();
+ BasicDeviceConfig cfg = networkConfigService.getConfig(did, BasicDeviceConfig.class);
+
+ if (!isAllowed(cfg)) {
+ kickOutBadDevice(did);
+ } else {
+ Device dev = getDevice(did);
+ DeviceDescription desc = (dev == null) ? null : BasicDeviceOperator.descriptionOf(dev);
+ desc = BasicDeviceOperator.combine(cfg, desc);
+ if (getProvider(did) != null) {
+ de = store.createOrUpdateDevice(getProvider(did).id(), did, desc);
+ }
+ }
+ }
+ if (event.configClass().equals(OpticalPortConfig.class)) {
+ ConnectPoint cpt = (ConnectPoint) event.subject();
+ DeviceId did = cpt.deviceId();
+ Port dpt = getPort(did, cpt.port());
+
+ if (dpt != null) {
+ OpticalPortConfig opc = networkConfigService.getConfig(cpt, OpticalPortConfig.class);
+ PortDescription desc = OpticalPortOperator.descriptionOf(dpt);
+ desc = OpticalPortOperator.combine(opc, desc);
+ if (getProvider(did) != null) {
+ de = store.updatePortStatus(getProvider(did).id(), did, desc);
+ }
+ }
+ }
+
+ if (de != null) {
+ post(de);
+ }
+ }
+
+ // checks if the specified device is allowed by the BasicDeviceConfig
+ // and if not, removes it
+ private void kickOutBadDevice(DeviceId deviceId) {
+ Device badDevice = getDevice(deviceId);
+ if (badDevice != null) {
+ removeDevice(deviceId);
+ }
+ }
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/OpticalPortOperator.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/OpticalPortOperator.java
new file mode 100644
index 00000000..b2fd02c7
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/OpticalPortOperator.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.device.impl;
+
+import static org.slf4j.LoggerFactory.getLogger;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.onosproject.net.config.ConfigOperator;
+import org.onosproject.net.config.basics.OpticalPortConfig;
+import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.OchPort;
+import org.onosproject.net.OduCltPort;
+import org.onosproject.net.OmsPort;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.SparseAnnotations;
+import org.onosproject.net.device.DefaultPortDescription;
+import org.onosproject.net.device.OchPortDescription;
+import org.onosproject.net.device.OduCltPortDescription;
+import org.onosproject.net.device.OmsPortDescription;
+import org.onosproject.net.device.PortDescription;
+import org.slf4j.Logger;
+
+/**
+ * Implementations of merge policies for various sources of optical port
+ * configuration information. This includes applications, provides, and network
+ * configurations.
+ */
+public final class OpticalPortOperator implements ConfigOperator {
+
+ private static final Logger log = getLogger(OpticalPortOperator.class);
+
+ private OpticalPortOperator() {
+ }
+
+ /**
+ * Generates a PortDescription containing fields from a PortDescription and
+ * an OpticalPortConfig.
+ *
+ * @param opc the port config entity from network config
+ * @param descr a PortDescription
+ * @return PortDescription based on both sources
+ */
+ public static PortDescription combine(OpticalPortConfig opc, PortDescription descr) {
+ if (opc == null) {
+ return descr;
+ }
+
+ PortNumber port = descr.portNumber();
+ final String name = opc.name();
+ final String numName = opc.numberName();
+ // if the description is null, or the current description port name != config name,
+ // create a new PortNumber.
+ PortNumber newPort = null;
+ if (port == null) {
+ // try to get the portNumber from the numName.
+ if (!numName.isEmpty()) {
+ final long pn = Long.parseLong(numName);
+ newPort = (!name.isEmpty()) ? PortNumber.portNumber(pn, name) : PortNumber.portNumber(pn);
+ } else {
+ // we don't have defining info (a port number value)
+ throw new RuntimeException("Possible misconfig, bailing on handling for: \n\t" + descr);
+ }
+ } else if ((!name.isEmpty()) && !name.equals(port.name())) {
+ final long pn = (numName.isEmpty()) ? port.toLong() : Long.parseLong(numName);
+ newPort = PortNumber.portNumber(pn, name);
+ }
+
+ // Port type won't change unless we're overwriting a port completely.
+ // Watch out for overwrites to avoid class cast craziness.
+ boolean noOwrite = opc.type() == descr.type();
+
+ SparseAnnotations sa = combine(opc, descr.annotations());
+ if (noOwrite) {
+ return updateDescription((newPort == null) ? port : newPort, sa, descr);
+ } else {
+ // TODO: must reconstruct a different type of PortDescription.
+ log.info("Type rewrite from {} to {} required", descr.type(), opc.type());
+ }
+ return descr;
+ }
+
+ // updates a port description whose port type has not changed.
+ private static PortDescription updateDescription(
+ PortNumber port, SparseAnnotations sa, PortDescription descr) {
+ switch (descr.type()) {
+ case OMS:
+ OmsPortDescription oms = (OmsPortDescription) descr;
+ return new OmsPortDescription(port, oms.isEnabled(), oms.minFrequency(),
+ oms.maxFrequency(), oms.grid(), sa);
+ case OCH:
+ // We might need to update lambda below with STATIC_LAMBDA.
+ OchPortDescription och = (OchPortDescription) descr;
+ return new OchPortDescription(port, och.isEnabled(), och.signalType(),
+ och.isTunable(), och.lambda(), sa);
+ case ODUCLT:
+ OduCltPortDescription odu = (OduCltPortDescription) descr;
+ return new OduCltPortDescription(port, odu.isEnabled(), odu.signalType(), sa);
+ case PACKET:
+ case FIBER:
+ return new DefaultPortDescription(port, descr.isEnabled(), descr.type(),
+ descr.portSpeed(), sa);
+ default:
+ // this includes copper ports.
+ log.warn("Unsupported optical port type {} - can't update", descr.type());
+ return descr;
+ }
+ }
+
+ /**
+ * Generates an annotation from an existing annotation and OptcalPortConfig.
+ *
+ * @param opc the port config entity from network config
+ * @param an the annotation
+ * @return annotation combining both sources
+ */
+ public static SparseAnnotations combine(OpticalPortConfig opc, SparseAnnotations an) {
+ DefaultAnnotations.Builder b = DefaultAnnotations.builder();
+ if (!opc.staticPort().isEmpty()) {
+ b.set(AnnotationKeys.STATIC_PORT, opc.staticPort());
+ }
+ if (opc.staticLambda().isPresent()) {
+ b.set(AnnotationKeys.STATIC_LAMBDA, String.valueOf(opc.staticLambda().get()));
+ }
+ // The following may not need to be carried.
+ if (!opc.name().isEmpty()) {
+ b.set(AnnotationKeys.PORT_NAME, opc.name());
+ }
+ return DefaultAnnotations.union(an, b.build());
+ }
+
+ /**
+ * Returns a description built from an existing port.
+ *
+ * @param port the device port
+ * @return a PortDescription based on the port
+ */
+ public static PortDescription descriptionOf(Port port) {
+ checkNotNull(port, "Must supply non-null Port");
+ final PortNumber ptn = port.number();
+ final boolean isup = port.isEnabled();
+ final SparseAnnotations an = (SparseAnnotations) port.annotations();
+ switch (port.type()) {
+ case OMS:
+ OmsPort oms = (OmsPort) port;
+ return new OmsPortDescription(ptn, isup, oms.minFrequency(),
+ oms.maxFrequency(), oms.grid(), an);
+ case OCH:
+ OchPort och = (OchPort) port;
+ return new OchPortDescription(ptn, isup, och.signalType(),
+ och.isTunable(), och.lambda(), an);
+ case ODUCLT:
+ OduCltPort odu = (OduCltPort) port;
+ return new OduCltPortDescription(ptn, isup, odu.signalType(), an);
+ default:
+ return new DefaultPortDescription(ptn, isup, port.type(), port.portSpeed(), an);
+ }
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/package-info.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/package-info.java
new file mode 100644
index 00000000..23c21378
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/device/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Core subsystem for tracking global inventory of infrastructure devices.
+ */
+package org.onosproject.net.device.impl;
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/driver/impl/DriverManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/driver/impl/DriverManager.java
new file mode 100644
index 00000000..53bf30a1
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/driver/impl/DriverManager.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.driver.impl;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.driver.Behaviour;
+import org.onosproject.net.driver.DefaultDriverData;
+import org.onosproject.net.driver.DefaultDriverHandler;
+import org.onosproject.net.driver.DefaultDriverProvider;
+import org.onosproject.net.driver.Driver;
+import org.onosproject.net.driver.DriverAdminService;
+import org.onosproject.net.driver.DriverHandler;
+import org.onosproject.net.driver.DriverProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static org.onlab.util.Tools.nullIsNotFound;
+import static org.onosproject.net.AnnotationKeys.DRIVER;
+import static org.onosproject.security.AppGuard.checkPermission;
+import static org.onosproject.security.AppPermission.Type.*;
+
+
+
+/**
+ * Manages inventory of device drivers.
+ */
+@Component(immediate = true)
+@Service
+public class DriverManager extends DefaultDriverProvider implements DriverAdminService {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private static final String NO_DRIVER = "Driver not found";
+ private static final String NO_DEVICE = "Device not found";
+ private static final String DEFAULT = "default";
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ private Set<DriverProvider> providers = Sets.newConcurrentHashSet();
+ private Map<String, Driver> driverByKey = Maps.newConcurrentMap();
+
+ @Activate
+ protected void activate() {
+ log.info("Started");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ log.info("Stopped");
+ }
+
+
+ @Override
+ public Set<DriverProvider> getProviders() {
+ return ImmutableSet.copyOf(providers);
+ }
+
+ @Override
+ public void registerProvider(DriverProvider provider) {
+ provider.getDrivers().forEach(driver -> {
+ addDrivers(provider.getDrivers());
+ driverByKey.put(key(driver.manufacturer(),
+ driver.hwVersion(),
+ driver.swVersion()), driver);
+ });
+ providers.add(provider);
+ }
+
+ @Override
+ public void unregisterProvider(DriverProvider provider) {
+ provider.getDrivers().forEach(driver -> {
+ removeDrivers(provider.getDrivers());
+ driverByKey.remove(key(driver.manufacturer(),
+ driver.hwVersion(),
+ driver.swVersion()));
+ });
+ providers.remove(provider);
+ }
+
+ @Override
+ public Set<Driver> getDrivers() {
+ checkPermission(DRIVER_READ);
+
+ ImmutableSet.Builder<Driver> builder = ImmutableSet.builder();
+ drivers.values().forEach(builder::add);
+ return builder.build();
+ }
+
+ @Override
+ public Set<Driver> getDrivers(Class<? extends Behaviour> withBehaviour) {
+ checkPermission(DRIVER_READ);
+
+ return drivers.values().stream()
+ .filter(d -> d.hasBehaviour(withBehaviour))
+ .collect(Collectors.toSet());
+ }
+
+ @Override
+ public Driver getDriver(String driverName) {
+ checkPermission(DRIVER_READ);
+
+ return nullIsNotFound(drivers.get(driverName), NO_DRIVER);
+ }
+
+ @Override
+ public Driver getDriver(String mfr, String hw, String sw) {
+ checkPermission(DRIVER_READ);
+
+ // First attempt a literal search.
+ Driver driver = driverByKey.get(key(mfr, hw, sw));
+ if (driver != null) {
+ return driver;
+ }
+
+ // Otherwise, sweep through the key space and attempt to match using
+ // regular expression matching.
+ Optional<Driver> optional = driverByKey.values().stream()
+ .filter(d -> matches(d, mfr, hw, sw)).findFirst();
+
+ // If no matching driver is found, return default.
+ return optional.isPresent() ? optional.get() : drivers.get(DEFAULT);
+ }
+
+ // Matches the given driver using ERE matching against the given criteria.
+ private boolean matches(Driver d, String mfr, String hw, String sw) {
+ // TODO: consider pre-compiling the expressions in the future
+ return mfr.matches(d.manufacturer()) &&
+ hw.matches(d.hwVersion()) &&
+ sw.matches(d.swVersion());
+ }
+
+ @Override
+ public Driver getDriver(DeviceId deviceId) {
+ checkPermission(DRIVER_READ);
+
+ Device device = nullIsNotFound(deviceService.getDevice(deviceId), NO_DEVICE);
+ String driverName = device.annotations().value(DRIVER);
+ if (driverName != null) {
+ return getDriver(driverName);
+ }
+ return nullIsNotFound(getDriver(device.manufacturer(),
+ device.hwVersion(), device.swVersion()),
+ NO_DRIVER);
+ }
+
+ @Override
+ public DriverHandler createHandler(DeviceId deviceId, String... credentials) {
+ checkPermission(DRIVER_WRITE);
+
+ Driver driver = getDriver(deviceId);
+ return new DefaultDriverHandler(new DefaultDriverData(driver, deviceId));
+ }
+
+ // Produces a composite driver key using the specified components.
+ private String key(String mfr, String hw, String sw) {
+ return String.format("%s-%s-%s", mfr, hw, sw);
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/driver/impl/package-info.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/driver/impl/package-info.java
new file mode 100644
index 00000000..2006391a
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/driver/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Implementation of the device driver management subsystem.
+ */
+package org.onosproject.net.driver.impl; \ No newline at end of file
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/edgeservice/impl/EdgeManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/edgeservice/impl/EdgeManager.java
new file mode 100644
index 00000000..e992f7a4
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/edgeservice/impl/EdgeManager.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.onosproject.net.edgeservice.impl;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.event.AbstractListenerManager;
+import org.onosproject.event.Event;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.edge.EdgePortEvent;
+import org.onosproject.net.edge.EdgePortListener;
+import org.onosproject.net.edge.EdgePortService;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.link.LinkEvent;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketService;
+import org.onosproject.net.topology.Topology;
+import org.onosproject.net.topology.TopologyEvent;
+import org.onosproject.net.topology.TopologyListener;
+import org.onosproject.net.topology.TopologyService;
+import org.slf4j.Logger;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+import static org.onosproject.net.device.DeviceEvent.Type.*;
+import static org.onosproject.net.edge.EdgePortEvent.Type.EDGE_PORT_ADDED;
+import static org.onosproject.net.edge.EdgePortEvent.Type.EDGE_PORT_REMOVED;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * This is an implementation of the edge net service.
+ */
+@Component(immediate = true)
+@Service
+public class EdgeManager
+ extends AbstractListenerManager<EdgePortEvent, EdgePortListener>
+ implements EdgePortService {
+
+ private final Logger log = getLogger(getClass());
+
+ private Topology topology;
+
+ private final Map<DeviceId, Set<ConnectPoint>> connectionPoints = Maps.newConcurrentMap();
+
+ private final TopologyListener topologyListener = new InnerTopologyListener();
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PacketService packetService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected TopologyService topologyService;
+
+ @Activate
+ public void activate() {
+ eventDispatcher.addSink(EdgePortEvent.class, listenerRegistry);
+ topologyService.addListener(topologyListener);
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ eventDispatcher.removeSink(EdgePortEvent.class);
+ topologyService.removeListener(topologyListener);
+ log.info("Stopped");
+ }
+
+ @Override
+ public boolean isEdgePoint(ConnectPoint point) {
+ return !topologyService.isInfrastructure(topologyService.currentTopology(), point);
+ }
+
+ @Override
+ public Iterable<ConnectPoint> getEdgePoints() {
+ ImmutableSet.Builder<ConnectPoint> builder = ImmutableSet.builder();
+ connectionPoints.forEach((k, v) -> v.forEach(builder::add));
+ return builder.build();
+ }
+
+ @Override
+ public Iterable<ConnectPoint> getEdgePoints(DeviceId deviceId) {
+ ImmutableSet.Builder<ConnectPoint> builder = ImmutableSet.builder();
+ Set<ConnectPoint> set = connectionPoints.get(deviceId);
+ if (set != null) {
+ set.forEach(builder::add);
+ }
+ return builder.build();
+ }
+
+ @Override
+ public void emitPacket(ByteBuffer data, Optional<TrafficTreatment> treatment) {
+ TrafficTreatment.Builder builder = treatment.isPresent() ?
+ DefaultTrafficTreatment.builder(treatment.get()) :
+ DefaultTrafficTreatment.builder();
+ getEdgePoints().forEach(p -> packetService.emit(packet(builder, p, data)));
+ }
+
+ @Override
+ public void emitPacket(DeviceId deviceId, ByteBuffer data,
+ Optional<TrafficTreatment> treatment) {
+ TrafficTreatment.Builder builder = treatment.isPresent() ?
+ DefaultTrafficTreatment.builder(treatment.get()) :
+ DefaultTrafficTreatment.builder();
+ getEdgePoints(deviceId).forEach(p -> packetService.emit(packet(builder, p, data)));
+ }
+
+ private OutboundPacket packet(TrafficTreatment.Builder builder, ConnectPoint point, ByteBuffer data) {
+ builder.setOutput(point.port());
+ return new DefaultOutboundPacket(point.deviceId(), builder.build(), data);
+ }
+
+ // Internal listener for topo events used to keep our edge-port cache
+ // up to date.
+ private class InnerTopologyListener implements TopologyListener {
+ @Override
+ public void event(TopologyEvent event) {
+ topology = event.subject();
+ List<Event> triggers = event.reasons();
+ if (triggers != null) {
+ triggers.forEach(reason -> {
+ if (reason instanceof DeviceEvent) {
+ processDeviceEvent((DeviceEvent) reason);
+ } else if (reason instanceof LinkEvent) {
+ processLinkEvent((LinkEvent) reason);
+ }
+ });
+ } else {
+ //FIXME special case of preexisting edgeport & no triggerless events could cause this to never hit and
+ //never discover an edgeport that should have been discovered.
+ loadAllEdgePorts();
+ }
+ }
+ }
+
+ // Initial loading of the edge port cache.
+ private void loadAllEdgePorts() {
+ deviceService.getAvailableDevices().forEach(d -> deviceService.getPorts(d.id())
+ .forEach(p -> addEdgePort(new ConnectPoint(d.id(), p.number()))));
+ }
+
+ // Processes a link event by adding or removing its end-points in our cache.
+ private void processLinkEvent(LinkEvent event) {
+ if (event.type() == LinkEvent.Type.LINK_ADDED) {
+ removeEdgePort(event.subject().src());
+ removeEdgePort(event.subject().dst());
+ } else if (event.type() == LinkEvent.Type.LINK_REMOVED) {
+ addEdgePort(event.subject().src());
+ addEdgePort(event.subject().dst());
+ }
+ }
+
+ // Processes a device event by adding or removing its end-points in our cache.
+ private void processDeviceEvent(DeviceEvent event) {
+ //FIXME handle the case where a device is suspended, this may or may not come up
+ DeviceEvent.Type type = event.type();
+ DeviceId id = event.subject().id();
+
+ if (type == DEVICE_ADDED ||
+ type == DEVICE_AVAILABILITY_CHANGED && deviceService.isAvailable(id)) {
+ // When device is added or becomes available, add all its ports
+ deviceService.getPorts(event.subject().id())
+ .forEach(p -> addEdgePort(new ConnectPoint(id, p.number())));
+ } else if (type == DEVICE_REMOVED ||
+ type == DEVICE_AVAILABILITY_CHANGED && !deviceService.isAvailable(id)) {
+ // When device is removed or becomes unavailable, remove all its ports
+ deviceService.getPorts(event.subject().id())
+ .forEach(p -> removeEdgePort(new ConnectPoint(id, p.number())));
+ connectionPoints.remove(id);
+
+ } else if (type == DeviceEvent.Type.PORT_ADDED ||
+ type == PORT_UPDATED && event.port().isEnabled()) {
+ addEdgePort(new ConnectPoint(id, event.port().number()));
+ } else if (type == DeviceEvent.Type.PORT_REMOVED ||
+ type == PORT_UPDATED && !event.port().isEnabled()) {
+ removeEdgePort(new ConnectPoint(id, event.port().number()));
+ }
+ }
+
+ // Adds the specified connection point to the edge points if needed.
+ private void addEdgePort(ConnectPoint point) {
+ if (!topologyService.isInfrastructure(topology, point) && !point.port().isLogical()) {
+ Set<ConnectPoint> set = connectionPoints.get(point.deviceId());
+ if (set == null) {
+ set = Sets.newConcurrentHashSet();
+ connectionPoints.put(point.deviceId(), set);
+ }
+ if (set.add(point)) {
+ post(new EdgePortEvent(EDGE_PORT_ADDED, point));
+ }
+ }
+ }
+
+ // Removes the specified connection point from the edge points.
+ private void removeEdgePort(ConnectPoint point) {
+ if (!point.port().isLogical()) {
+ Set<ConnectPoint> set = connectionPoints.get(point.deviceId());
+ if (set == null) {
+ return;
+ }
+ if (set.remove(point)) {
+ post(new EdgePortEvent(EDGE_PORT_REMOVED, point));
+ }
+ if (set.isEmpty()) {
+ connectionPoints.remove(point.deviceId());
+ }
+ }
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/edgeservice/impl/package-info.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/edgeservice/impl/package-info.java
new file mode 100644
index 00000000..fd867326
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/edgeservice/impl/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Core subsystem for interacting with network edges.
+ */
+package org.onosproject.net.edgeservice.impl; \ No newline at end of file
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/flow/impl/FlowRuleManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/flow/impl/FlowRuleManager.java
new file mode 100644
index 00000000..a1d046c5
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/flow/impl/FlowRuleManager.java
@@ -0,0 +1,593 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.flow.impl;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Modified;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.util.Tools;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.net.provider.AbstractListenerProviderRegistry;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.core.IdGenerator;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.CompletedBatchOperation;
+import org.onosproject.net.flow.DefaultFlowEntry;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleBatchEntry;
+import org.onosproject.net.flow.FlowRuleBatchEvent;
+import org.onosproject.net.flow.FlowRuleBatchOperation;
+import org.onosproject.net.flow.FlowRuleBatchRequest;
+import org.onosproject.net.flow.FlowRuleEvent;
+import org.onosproject.net.flow.FlowRuleListener;
+import org.onosproject.net.flow.FlowRuleOperation;
+import org.onosproject.net.flow.FlowRuleOperations;
+import org.onosproject.net.flow.FlowRuleOperationsContext;
+import org.onosproject.net.flow.FlowRuleProvider;
+import org.onosproject.net.flow.FlowRuleProviderRegistry;
+import org.onosproject.net.flow.FlowRuleProviderService;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.FlowRuleStore;
+import org.onosproject.net.flow.FlowRuleStoreDelegate;
+import org.onosproject.net.provider.AbstractProviderService;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.net.flow.FlowRuleEvent.Type.RULE_ADD_REQUESTED;
+import static org.onosproject.net.flow.FlowRuleEvent.Type.RULE_REMOVE_REQUESTED;
+import static org.onosproject.security.AppGuard.checkPermission;
+import static org.slf4j.LoggerFactory.getLogger;
+import static org.onosproject.security.AppPermission.Type.*;
+
+
+
+/**
+ * Provides implementation of the flow NB &amp; SB APIs.
+ */
+@Component(immediate = true, enabled = true)
+@Service
+public class FlowRuleManager
+ extends AbstractListenerProviderRegistry<FlowRuleEvent, FlowRuleListener,
+ FlowRuleProvider, FlowRuleProviderService>
+ implements FlowRuleService, FlowRuleProviderRegistry {
+
+ public static final String FLOW_RULE_NULL = "FlowRule cannot be null";
+ private static final boolean ALLOW_EXTRANEOUS_RULES = false;
+
+ @Property(name = "allowExtraneousRules", boolValue = ALLOW_EXTRANEOUS_RULES,
+ label = "Allow flow rules in switch not installed by ONOS")
+ private boolean allowExtraneousRules = ALLOW_EXTRANEOUS_RULES;
+
+ private final Logger log = getLogger(getClass());
+
+ private final FlowRuleStoreDelegate delegate = new InternalStoreDelegate();
+
+ protected ExecutorService deviceInstallers =
+ Executors.newFixedThreadPool(32, groupedThreads("onos/flowservice", "device-installer-%d"));
+
+ protected ExecutorService operationsService =
+ Executors.newFixedThreadPool(32, groupedThreads("onos/flowservice", "operations-%d"));
+
+ private IdGenerator idGenerator;
+
+ private Map<Long, FlowOperationsProcessor> pendingFlowOperations
+ = new ConcurrentHashMap<>();
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowRuleStore store;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ComponentConfigService cfgService;
+
+ @Activate
+ public void activate(ComponentContext context) {
+ cfgService.registerProperties(getClass());
+ idGenerator = coreService.getIdGenerator(FLOW_OP_TOPIC);
+
+ modified(context);
+
+ store.setDelegate(delegate);
+ eventDispatcher.addSink(FlowRuleEvent.class, listenerRegistry);
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ cfgService.unregisterProperties(getClass(), false);
+ deviceInstallers.shutdownNow();
+ operationsService.shutdownNow();
+ store.unsetDelegate(delegate);
+ eventDispatcher.removeSink(FlowRuleEvent.class);
+ log.info("Stopped");
+ }
+
+ @Modified
+ public void modified(ComponentContext context) {
+ if (context == null) {
+ return;
+ }
+
+ Dictionary<?, ?> properties = context.getProperties();
+
+ String s = Tools.get(properties, "allowExtraneousRules");
+ allowExtraneousRules = Strings.isNullOrEmpty(s) ? ALLOW_EXTRANEOUS_RULES : Boolean.valueOf(s);
+
+ if (allowExtraneousRules) {
+ log.info("Allowing flow rules not installed by ONOS");
+ }
+ }
+
+ @Override
+ public int getFlowRuleCount() {
+ checkPermission(FLOWRULE_READ);
+ return store.getFlowRuleCount();
+ }
+
+ @Override
+ public Iterable<FlowEntry> getFlowEntries(DeviceId deviceId) {
+ checkPermission(FLOWRULE_READ);
+ return store.getFlowEntries(deviceId);
+ }
+
+ @Override
+ public void applyFlowRules(FlowRule... flowRules) {
+ checkPermission(FLOWRULE_WRITE);
+
+ FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
+ for (int i = 0; i < flowRules.length; i++) {
+ builder.add(flowRules[i]);
+ }
+ apply(builder.build());
+ }
+
+ @Override
+ public void removeFlowRules(FlowRule... flowRules) {
+ checkPermission(FLOWRULE_WRITE);
+
+ FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
+ for (int i = 0; i < flowRules.length; i++) {
+ builder.remove(flowRules[i]);
+ }
+ apply(builder.build());
+ }
+
+ @Override
+ public void removeFlowRulesById(ApplicationId id) {
+ checkPermission(FLOWRULE_WRITE);
+ removeFlowRules(Iterables.toArray(getFlowRulesById(id), FlowRule.class));
+ }
+
+ @Override
+ public Iterable<FlowRule> getFlowRulesById(ApplicationId id) {
+ checkPermission(FLOWRULE_READ);
+
+ Set<FlowRule> flowEntries = Sets.newHashSet();
+ for (Device d : deviceService.getDevices()) {
+ for (FlowEntry flowEntry : store.getFlowEntries(d.id())) {
+ if (flowEntry.appId() == id.id()) {
+ flowEntries.add(flowEntry);
+ }
+ }
+ }
+ return flowEntries;
+ }
+
+ @Override
+ public Iterable<FlowRule> getFlowRulesByGroupId(ApplicationId appId, short groupId) {
+ checkPermission(FLOWRULE_READ);
+
+ Set<FlowRule> matches = Sets.newHashSet();
+ long toLookUp = ((long) appId.id() << 16) | groupId;
+ for (Device d : deviceService.getDevices()) {
+ for (FlowEntry flowEntry : store.getFlowEntries(d.id())) {
+ if ((flowEntry.id().value() >>> 32) == toLookUp) {
+ matches.add(flowEntry);
+ }
+ }
+ }
+ return matches;
+ }
+
+ @Override
+ public void apply(FlowRuleOperations ops) {
+ checkPermission(FLOWRULE_WRITE);
+ operationsService.submit(new FlowOperationsProcessor(ops));
+ }
+
+ @Override
+ protected FlowRuleProviderService createProviderService(
+ FlowRuleProvider provider) {
+ return new InternalFlowRuleProviderService(provider);
+ }
+
+ private class InternalFlowRuleProviderService
+ extends AbstractProviderService<FlowRuleProvider>
+ implements FlowRuleProviderService {
+
+ final Map<FlowEntry, Long> lastSeen = Maps.newConcurrentMap();
+
+ protected InternalFlowRuleProviderService(FlowRuleProvider provider) {
+ super(provider);
+ }
+
+ @Override
+ public void flowRemoved(FlowEntry flowEntry) {
+ checkNotNull(flowEntry, FLOW_RULE_NULL);
+ checkValidity();
+ lastSeen.remove(flowEntry);
+ FlowEntry stored = store.getFlowEntry(flowEntry);
+ if (stored == null) {
+ log.debug("Rule already evicted from store: {}", flowEntry);
+ return;
+ }
+ Device device = deviceService.getDevice(flowEntry.deviceId());
+ FlowRuleProvider frp = getProvider(device.providerId());
+ FlowRuleEvent event = null;
+ switch (stored.state()) {
+ case ADDED:
+ case PENDING_ADD:
+ frp.applyFlowRule(stored);
+ break;
+ case PENDING_REMOVE:
+ case REMOVED:
+ event = store.removeFlowRule(stored);
+ break;
+ default:
+ break;
+
+ }
+ if (event != null) {
+ log.debug("Flow {} removed", flowEntry);
+ post(event);
+ }
+ }
+
+
+ private void flowMissing(FlowEntry flowRule) {
+ checkNotNull(flowRule, FLOW_RULE_NULL);
+ checkValidity();
+ Device device = deviceService.getDevice(flowRule.deviceId());
+ FlowRuleProvider frp = getProvider(device.providerId());
+ FlowRuleEvent event = null;
+ switch (flowRule.state()) {
+ case PENDING_REMOVE:
+ case REMOVED:
+ event = store.removeFlowRule(flowRule);
+ frp.removeFlowRule(flowRule);
+ break;
+ case ADDED:
+ case PENDING_ADD:
+ try {
+ frp.applyFlowRule(flowRule);
+ } catch (UnsupportedOperationException e) {
+ log.warn(e.getMessage());
+ if (flowRule instanceof DefaultFlowEntry) {
+ ((DefaultFlowEntry) flowRule).setState(FlowEntry.FlowEntryState.FAILED);
+ }
+ }
+ break;
+ default:
+ log.debug("Flow {} has not been installed.", flowRule);
+ }
+
+ if (event != null) {
+ log.debug("Flow {} removed", flowRule);
+ post(event);
+ }
+
+ }
+
+
+ private void extraneousFlow(FlowRule flowRule) {
+ checkNotNull(flowRule, FLOW_RULE_NULL);
+ checkValidity();
+ FlowRuleProvider frp = getProvider(flowRule.deviceId());
+ frp.removeFlowRule(flowRule);
+ log.debug("Flow {} is on switch but not in store.", flowRule);
+ }
+
+
+ private void flowAdded(FlowEntry flowEntry) {
+ checkNotNull(flowEntry, FLOW_RULE_NULL);
+ checkValidity();
+
+ if (checkRuleLiveness(flowEntry, store.getFlowEntry(flowEntry))) {
+
+ FlowRuleEvent event = store.addOrUpdateFlowRule(flowEntry);
+ if (event == null) {
+ log.debug("No flow store event generated.");
+ } else {
+ log.trace("Flow {} {}", flowEntry, event.type());
+ post(event);
+ }
+ } else {
+ log.debug("Removing flow rules....");
+ removeFlowRules(flowEntry);
+ }
+
+ }
+
+ private boolean checkRuleLiveness(FlowEntry swRule, FlowEntry storedRule) {
+ if (storedRule == null) {
+ return false;
+ }
+ if (storedRule.isPermanent()) {
+ return true;
+ }
+
+ final long timeout = storedRule.timeout() * 1000;
+ final long currentTime = System.currentTimeMillis();
+ if (storedRule.packets() != swRule.packets()) {
+ lastSeen.put(storedRule, currentTime);
+ return true;
+ }
+ if (!lastSeen.containsKey(storedRule)) {
+ // checking for the first time
+ lastSeen.put(storedRule, storedRule.lastSeen());
+ // Use following if lastSeen attr. was removed.
+ //lastSeen.put(storedRule, currentTime);
+ }
+ Long last = lastSeen.get(storedRule);
+ if (last == null) {
+ // concurrently removed? let the liveness check fail
+ return false;
+ }
+
+ if ((currentTime - last) <= timeout) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void pushFlowMetrics(DeviceId deviceId, Iterable<FlowEntry> flowEntries) {
+ Map<FlowEntry, FlowEntry> storedRules = Maps.newHashMap();
+ store.getFlowEntries(deviceId).forEach(f -> storedRules.put(f, f));
+
+ for (FlowEntry rule : flowEntries) {
+ try {
+ FlowEntry storedRule = storedRules.remove(rule);
+ if (storedRule != null) {
+ if (storedRule.exactMatch(rule)) {
+ // we both have the rule, let's update some info then.
+ flowAdded(rule);
+ } else {
+ // the two rules are not an exact match - remove the
+ // switch's rule and install our rule
+ extraneousFlow(rule);
+ flowMissing(storedRule);
+ }
+ } else {
+ // the device has a rule the store does not have
+ if (!allowExtraneousRules) {
+ extraneousFlow(rule);
+ }
+ }
+ } catch (Exception e) {
+ log.debug("Can't process added or extra rule {}", e.getMessage());
+ continue;
+ }
+ }
+ for (FlowEntry rule : storedRules.keySet()) {
+ try {
+ // there are rules in the store that aren't on the switch
+ log.debug("Adding rule in store, but not on switch {}", rule);
+ flowMissing(rule);
+ } catch (Exception e) {
+ log.debug("Can't add missing flow rule {}", e.getMessage());
+ continue;
+ }
+ }
+
+ }
+
+ @Override
+ public void batchOperationCompleted(long batchId, CompletedBatchOperation operation) {
+ store.batchOperationComplete(FlowRuleBatchEvent.completed(
+ new FlowRuleBatchRequest(batchId, Collections.emptySet()),
+ operation
+ ));
+ }
+ }
+
+ // Store delegate to re-post events emitted from the store.
+ private class InternalStoreDelegate implements FlowRuleStoreDelegate {
+
+
+ // TODO: Right now we only dispatch events at individual flowEntry level.
+ // It may be more efficient for also dispatch events as a batch.
+ @Override
+ public void notify(FlowRuleBatchEvent event) {
+ final FlowRuleBatchRequest request = event.subject();
+ switch (event.type()) {
+ case BATCH_OPERATION_REQUESTED:
+ // Request has been forwarded to MASTER Node, and was
+ request.ops().stream().forEach(
+ op -> {
+ switch (op.operator()) {
+
+ case ADD:
+ post(new FlowRuleEvent(RULE_ADD_REQUESTED,
+ op.target()));
+ break;
+ case REMOVE:
+ post(new FlowRuleEvent(RULE_REMOVE_REQUESTED,
+ op.target()));
+ break;
+ case MODIFY:
+ //TODO: do something here when the time comes.
+ break;
+ default:
+ log.warn("Unknown flow operation operator: {}", op.operator());
+ }
+ }
+ );
+
+ DeviceId deviceId = event.deviceId();
+
+ FlowRuleBatchOperation batchOperation =
+ request.asBatchOperation(deviceId);
+
+ FlowRuleProvider flowRuleProvider = getProvider(deviceId);
+ if (flowRuleProvider != null) {
+ flowRuleProvider.executeBatch(batchOperation);
+ }
+
+ break;
+
+ case BATCH_OPERATION_COMPLETED:
+
+ FlowOperationsProcessor fops = pendingFlowOperations.remove(
+ event.subject().batchId());
+ if (event.result().isSuccess()) {
+ if (fops != null) {
+ fops.satisfy(event.deviceId());
+ }
+ } else {
+ fops.fail(event.deviceId(), event.result().failedItems());
+ }
+
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ private class FlowOperationsProcessor implements Runnable {
+
+ private final List<Set<FlowRuleOperation>> stages;
+ private final FlowRuleOperationsContext context;
+ private final FlowRuleOperations fops;
+ private final AtomicBoolean hasFailed = new AtomicBoolean(false);
+
+ private Set<DeviceId> pendingDevices;
+
+ public FlowOperationsProcessor(FlowRuleOperations ops) {
+ this.stages = Lists.newArrayList(ops.stages());
+ this.context = ops.callback();
+ this.fops = ops;
+ pendingDevices = Sets.newConcurrentHashSet();
+ }
+
+ @Override
+ public void run() {
+ if (stages.size() > 0) {
+ process(stages.remove(0));
+ } else if (!hasFailed.get() && context != null) {
+ context.onSuccess(fops);
+ }
+ }
+
+ private void process(Set<FlowRuleOperation> ops) {
+ Multimap<DeviceId, FlowRuleBatchEntry> perDeviceBatches =
+ ArrayListMultimap.create();
+
+ FlowRuleBatchEntry fbe;
+ for (FlowRuleOperation flowRuleOperation : ops) {
+ switch (flowRuleOperation.type()) {
+ // FIXME: Brian needs imagination when creating class names.
+ case ADD:
+ fbe = new FlowRuleBatchEntry(
+ FlowRuleBatchEntry.FlowRuleOperation.ADD, flowRuleOperation.rule());
+ break;
+ case MODIFY:
+ fbe = new FlowRuleBatchEntry(
+ FlowRuleBatchEntry.FlowRuleOperation.MODIFY, flowRuleOperation.rule());
+ break;
+ case REMOVE:
+ fbe = new FlowRuleBatchEntry(
+ FlowRuleBatchEntry.FlowRuleOperation.REMOVE, flowRuleOperation.rule());
+ break;
+ default:
+ throw new UnsupportedOperationException("Unknown flow rule type " + flowRuleOperation.type());
+ }
+ pendingDevices.add(flowRuleOperation.rule().deviceId());
+ perDeviceBatches.put(flowRuleOperation.rule().deviceId(), fbe);
+ }
+
+
+ for (DeviceId deviceId : perDeviceBatches.keySet()) {
+ long id = idGenerator.getNewId();
+ final FlowRuleBatchOperation b = new FlowRuleBatchOperation(perDeviceBatches.get(deviceId),
+ deviceId, id);
+ pendingFlowOperations.put(id, this);
+ deviceInstallers.submit(() -> store.storeBatch(b));
+ }
+ }
+
+ public void satisfy(DeviceId devId) {
+ pendingDevices.remove(devId);
+ if (pendingDevices.isEmpty()) {
+ operationsService.submit(this);
+ }
+ }
+
+
+
+ public void fail(DeviceId devId, Set<? extends FlowRule> failures) {
+ hasFailed.set(true);
+ pendingDevices.remove(devId);
+ if (pendingDevices.isEmpty()) {
+ operationsService.submit(this);
+ }
+
+ if (context != null) {
+ final FlowRuleOperations.Builder failedOpsBuilder =
+ FlowRuleOperations.builder();
+ failures.stream().forEach(failedOpsBuilder::add);
+
+ context.onError(failedOpsBuilder.build());
+ }
+ }
+
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/flow/impl/package-info.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/flow/impl/package-info.java
new file mode 100644
index 00000000..69934b6f
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/flow/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Core subsystem for tracking and manipulating global flow state.
+ */
+package org.onosproject.net.flow.impl;
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/FlowObjectiveManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/FlowObjectiveManager.java
new file mode 100644
index 00000000..a76a298f
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/FlowObjectiveManager.java
@@ -0,0 +1,416 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.flowobjective.impl;
+
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.osgi.DefaultServiceDirectory;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.util.ItemNotFoundException;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.mastership.MastershipEvent;
+import org.onosproject.mastership.MastershipListener;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.behaviour.Pipeliner;
+import org.onosproject.net.behaviour.PipelinerContext;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.driver.DefaultDriverProviderService;
+import org.onosproject.net.driver.DriverHandler;
+import org.onosproject.net.driver.DriverService;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flowobjective.FilteringObjective;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.FlowObjectiveStore;
+import org.onosproject.net.flowobjective.FlowObjectiveStoreDelegate;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flowobjective.NextObjective;
+import org.onosproject.net.flowobjective.Objective;
+import org.onosproject.net.flowobjective.ObjectiveError;
+import org.onosproject.net.flowobjective.ObjectiveEvent;
+import org.onosproject.net.group.GroupService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.concurrent.Executors.newFixedThreadPool;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.security.AppGuard.checkPermission;
+import static org.onosproject.security.AppPermission.Type.*;
+
+
+
+/**
+ * Provides implementation of the flow objective programming service.
+ */
+@Component(immediate = true)
+@Service
+public class FlowObjectiveManager implements FlowObjectiveService {
+
+ public static final int INSTALL_RETRY_ATTEMPTS = 5;
+ public static final long INSTALL_RETRY_INTERVAL = 1000; // ms
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DriverService driverService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected MastershipService mastershipService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ClusterService clusterService;
+
+ // Note: The following dependencies are added on behalf of the pipeline
+ // driver behaviours to assure these services are available for their
+ // initialization.
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowRuleService flowRuleService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected GroupService groupService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowObjectiveStore flowObjectiveStore;
+
+ // Note: This must remain an optional dependency to allow re-install of default drivers.
+ // Note: For now disabled until we can move to OPTIONAL_UNARY dependency
+ // @Reference(cardinality = ReferenceCardinality.OPTIONAL_UNARY, policy = ReferencePolicy.DYNAMIC)
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DefaultDriverProviderService defaultDriverService;
+
+ private final FlowObjectiveStoreDelegate delegate = new InternalStoreDelegate();
+
+ private final Map<DeviceId, DriverHandler> driverHandlers = Maps.newConcurrentMap();
+ private final Map<DeviceId, Pipeliner> pipeliners = Maps.newConcurrentMap();
+
+ private final PipelinerContext context = new InnerPipelineContext();
+ private final MastershipListener mastershipListener = new InnerMastershipListener();
+ private final DeviceListener deviceListener = new InnerDeviceListener();
+
+ protected ServiceDirectory serviceDirectory = new DefaultServiceDirectory();
+
+ private Map<Integer, Set<PendingNext>> pendingForwards = Maps.newConcurrentMap();
+
+ private ExecutorService executorService;
+
+ @Activate
+ protected void activate() {
+ executorService = newFixedThreadPool(4, groupedThreads("onos/objective-installer", "%d"));
+ flowObjectiveStore.setDelegate(delegate);
+ mastershipService.addListener(mastershipListener);
+ deviceService.addListener(deviceListener);
+ deviceService.getDevices().forEach(device -> setupPipelineHandler(device.id()));
+ log.info("Started");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ flowObjectiveStore.unsetDelegate(delegate);
+ mastershipService.removeListener(mastershipListener);
+ deviceService.removeListener(deviceListener);
+ executorService.shutdown();
+ pipeliners.clear();
+ driverHandlers.clear();
+ log.info("Stopped");
+ }
+
+ /**
+ * Task that passes the flow objective down to the driver. The task will
+ * make a few attempts to find the appropriate driver, then eventually give
+ * up and report an error if no suitable driver could be found.
+ */
+ private class ObjectiveInstaller implements Runnable {
+ private final DeviceId deviceId;
+ private final Objective objective;
+
+ private final int numAttempts;
+
+ public ObjectiveInstaller(DeviceId deviceId, Objective objective) {
+ this(deviceId, objective, 1);
+ }
+
+ public ObjectiveInstaller(DeviceId deviceId, Objective objective, int attemps) {
+ this.deviceId = checkNotNull(deviceId);
+ this.objective = checkNotNull(objective);
+ this.numAttempts = checkNotNull(attemps);
+ }
+
+ @Override
+ public void run() {
+ try {
+ Pipeliner pipeliner = getDevicePipeliner(deviceId);
+
+ if (pipeliner != null) {
+ if (objective instanceof NextObjective) {
+ pipeliner.next((NextObjective) objective);
+ } else if (objective instanceof ForwardingObjective) {
+ pipeliner.forward((ForwardingObjective) objective);
+ } else {
+ pipeliner.filter((FilteringObjective) objective);
+ }
+ } else if (numAttempts < INSTALL_RETRY_ATTEMPTS) {
+ Thread.sleep(INSTALL_RETRY_INTERVAL);
+ executorService.submit(new ObjectiveInstaller(deviceId, objective, numAttempts + 1));
+ } else {
+ // Otherwise we've tried a few times and failed, report an
+ // error back to the user.
+ objective.context().ifPresent(
+ c -> c.onError(objective, ObjectiveError.DEVICEMISSING));
+ }
+ } catch (Exception e) {
+ log.warn("Exception while installing flow objective", e);
+ }
+ }
+ }
+
+ @Override
+ public void filter(DeviceId deviceId, FilteringObjective filteringObjective) {
+ checkPermission(FLOWRULE_WRITE);
+ executorService.submit(new ObjectiveInstaller(deviceId, filteringObjective));
+ }
+
+ @Override
+ public void forward(DeviceId deviceId, ForwardingObjective forwardingObjective) {
+ checkPermission(FLOWRULE_WRITE);
+ if (queueObjective(deviceId, forwardingObjective)) {
+ return;
+ }
+ executorService.submit(new ObjectiveInstaller(deviceId, forwardingObjective));
+ }
+
+ @Override
+ public void next(DeviceId deviceId, NextObjective nextObjective) {
+ checkPermission(FLOWRULE_WRITE);
+ executorService.submit(new ObjectiveInstaller(deviceId, nextObjective));
+ }
+
+ @Override
+ public int allocateNextId() {
+ checkPermission(FLOWRULE_WRITE);
+ return flowObjectiveStore.allocateNextId();
+ }
+
+ @Override
+ public void initPolicy(String policy) {}
+
+ private boolean queueObjective(DeviceId deviceId, ForwardingObjective fwd) {
+ if (fwd.nextId() != null &&
+ flowObjectiveStore.getNextGroup(fwd.nextId()) == null) {
+ log.trace("Queuing forwarding objective for nextId {}", fwd.nextId());
+ if (pendingForwards.putIfAbsent(fwd.nextId(),
+ Sets.newHashSet(new PendingNext(deviceId, fwd))) != null) {
+ Set<PendingNext> pending = pendingForwards.get(fwd.nextId());
+ pending.add(new PendingNext(deviceId, fwd));
+ }
+ return true;
+ }
+ return false;
+ }
+
+ // Retrieves the device pipeline behaviour from the cache.
+ private Pipeliner getDevicePipeliner(DeviceId deviceId) {
+ return pipeliners.get(deviceId);
+ }
+
+ private void setupPipelineHandler(DeviceId deviceId) {
+ if (defaultDriverService == null) {
+ // We're not ready to go to work yet.
+ return;
+ }
+
+ // Attempt to lookup the handler in the cache
+ DriverHandler handler = driverHandlers.get(deviceId);
+ cTime = now();
+
+ if (handler == null) {
+ try {
+ // Otherwise create it and if it has pipeline behaviour, cache it
+ handler = driverService.createHandler(deviceId);
+ dTime = now();
+ if (!handler.driver().hasBehaviour(Pipeliner.class)) {
+ log.warn("Pipeline behaviour not supported for device {}",
+ deviceId);
+ return;
+ }
+ } catch (ItemNotFoundException e) {
+ log.warn("No applicable driver for device {}", deviceId);
+ return;
+ }
+
+ driverHandlers.put(deviceId, handler);
+ eTime = now();
+ }
+
+ // Always (re)initialize the pipeline behaviour
+ log.info("Driver {} bound to device {} ... initializing driver",
+ handler.driver().name(), deviceId);
+ hTime = now();
+ Pipeliner pipeliner = handler.behaviour(Pipeliner.class);
+ hbTime = now();
+ pipeliner.init(deviceId, context);
+ pipeliners.putIfAbsent(deviceId, pipeliner);
+ }
+
+ // Triggers driver setup when the local node becomes a device master.
+ private class InnerMastershipListener implements MastershipListener {
+ @Override
+ public void event(MastershipEvent event) {
+ switch (event.type()) {
+ case MASTER_CHANGED:
+ log.debug("mastership changed on device {}", event.subject());
+ start = now();
+ if (deviceService.isAvailable(event.subject())) {
+ setupPipelineHandler(event.subject());
+ }
+ stopWatch();
+ break;
+ case BACKUPS_CHANGED:
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ // Triggers driver setup when a device is (re)detected.
+ private class InnerDeviceListener implements DeviceListener {
+ @Override
+ public void event(DeviceEvent event) {
+ switch (event.type()) {
+ case DEVICE_ADDED:
+ case DEVICE_AVAILABILITY_CHANGED:
+ log.debug("Device either added or availability changed {}",
+ event.subject().id());
+ start = now();
+ if (deviceService.isAvailable(event.subject().id())) {
+ log.debug("Device is now available {}", event.subject().id());
+ setupPipelineHandler(event.subject().id());
+ }
+ stopWatch();
+ break;
+ case DEVICE_UPDATED:
+ break;
+ case DEVICE_REMOVED:
+ break;
+ case DEVICE_SUSPENDED:
+ break;
+ case PORT_ADDED:
+ break;
+ case PORT_UPDATED:
+ break;
+ case PORT_REMOVED:
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ // Temporary mechanism to monitor pipeliner setup time-cost; there are
+ // intermittent time where this takes in excess of 2 seconds. Why?
+ private long start = 0, totals = 0, count = 0;
+ private long cTime, dTime, eTime, hTime, hbTime;
+ private static final long LIMIT = 500;
+
+ private long now() {
+ return System.currentTimeMillis();
+ }
+
+ private void stopWatch() {
+ long duration = System.currentTimeMillis() - start;
+ totals += duration;
+ count += 1;
+ if (duration > LIMIT) {
+ log.info("Pipeline setup took {} ms; avg {} ms; cTime={}, dTime={}, eTime={}, hTime={}, hbTime={}",
+ duration, totals / count, diff(cTime), diff(dTime), diff(eTime), diff(hTime), diff(hbTime));
+ }
+ }
+
+ private long diff(long bTime) {
+ long diff = bTime - start;
+ return diff < 0 ? 0 : diff;
+ }
+
+ // Processing context for initializing pipeline driver behaviours.
+ private class InnerPipelineContext implements PipelinerContext {
+ @Override
+ public ServiceDirectory directory() {
+ return serviceDirectory;
+ }
+
+ @Override
+ public FlowObjectiveStore store() {
+ return flowObjectiveStore;
+ }
+ }
+
+ private class InternalStoreDelegate implements FlowObjectiveStoreDelegate {
+ @Override
+ public void notify(ObjectiveEvent event) {
+ log.debug("Received notification of obj event {}", event);
+ Set<PendingNext> pending = pendingForwards.remove(event.subject());
+
+ if (pending == null) {
+ log.debug("Nothing pending for this obj event");
+ return;
+ }
+
+ log.debug("Processing pending forwarding objectives {}", pending.size());
+
+ pending.forEach(p -> getDevicePipeliner(p.deviceId())
+ .forward(p.forwardingObjective()));
+
+ }
+ }
+
+ /**
+ * Data class used to hold a pending forwarding objective that could not
+ * be processed because the associated next object was not present.
+ */
+ private class PendingNext {
+ private final DeviceId deviceId;
+ private final ForwardingObjective fwd;
+
+ public PendingNext(DeviceId deviceId, ForwardingObjective fwd) {
+ this.deviceId = deviceId;
+ this.fwd = fwd;
+ }
+
+ public DeviceId deviceId() {
+ return deviceId;
+ }
+
+ public ForwardingObjective forwardingObjective() {
+ return fwd;
+ }
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FilterTable.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FilterTable.java
new file mode 100644
index 00000000..b46ce8b3
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FilterTable.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.flowobjective.impl.composition;
+
+import org.onosproject.net.flowobjective.FilteringObjective;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Provides a table to store Fitler.
+ */
+public class FilterTable {
+
+ protected Map<Integer, FilteringObjective> filterMap;
+
+ public FilterTable() {
+ this.filterMap = new HashMap<>();
+ }
+
+ public List<FilteringObjective> updateFilter(FilteringObjective filteringObjective) {
+ List<FilteringObjective> updates = new ArrayList<>();
+ switch (filteringObjective.op()) {
+ case ADD:
+ this.filterMap.put(filteringObjective.id(), filteringObjective);
+ updates.add(filteringObjective);
+ break;
+ case REMOVE:
+ this.filterMap.remove(filteringObjective.id());
+ updates.add(filteringObjective);
+ break;
+ default:
+ break;
+ }
+ return updates;
+ }
+
+ public List<FilteringObjective> updateFilter(List<FilteringObjective> filteringObjectives) {
+ List<FilteringObjective> updates = new ArrayList<>();
+ for (FilteringObjective filteringObjective : filteringObjectives) {
+ updates.addAll(this.updateFilter(filteringObjective));
+ }
+ return updates;
+ }
+
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionManager.java
new file mode 100644
index 00000000..3ef98bd8
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionManager.java
@@ -0,0 +1,439 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.flowobjective.impl.composition;
+
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.osgi.DefaultServiceDirectory;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.util.ItemNotFoundException;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.mastership.MastershipEvent;
+import org.onosproject.mastership.MastershipListener;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.behaviour.Pipeliner;
+import org.onosproject.net.behaviour.PipelinerContext;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.driver.DefaultDriverProviderService;
+import org.onosproject.net.driver.DriverHandler;
+import org.onosproject.net.driver.DriverService;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flowobjective.FilteringObjective;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.FlowObjectiveStore;
+import org.onosproject.net.flowobjective.FlowObjectiveStoreDelegate;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flowobjective.NextObjective;
+import org.onosproject.net.flowobjective.Objective;
+import org.onosproject.net.flowobjective.ObjectiveError;
+import org.onosproject.net.flowobjective.ObjectiveEvent;
+import org.onosproject.net.group.GroupService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.concurrent.Executors.newFixedThreadPool;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.security.AppGuard.checkPermission;
+import static org.onosproject.security.AppPermission.Type.*;
+
+
+/**
+ * Provides implementation of the flow objective programming service with composition feature.
+ *
+ * Note: This is an experimental, alternative implementation of the FlowObjectiveManager
+ * that supports composition. It can be enabled by setting the enable flag below to true,
+ * and you should also add "enabled = false" to the FlowObjectiveManager.
+ *
+ * The implementation relies a FlowObjectiveCompositionTree that is not yet distributed,
+ * so it will not have high availability and may break if device mastership changes.
+ * Therefore, it is safest to use this component in a single instance scenario.
+ * This comment will be removed when a distributed implementation is available.
+ */
+@Component(immediate = true, enabled = false)
+@Service
+public class FlowObjectiveCompositionManager implements FlowObjectiveService {
+
+ public enum PolicyOperator {
+ Parallel,
+ Sequential,
+ Override,
+ Application
+ }
+
+ public static final int INSTALL_RETRY_ATTEMPTS = 5;
+ public static final long INSTALL_RETRY_INTERVAL = 1000; // ms
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DriverService driverService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected MastershipService mastershipService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ClusterService clusterService;
+
+ // Note: The following dependencies are added on behalf of the pipeline
+ // driver behaviours to assure these services are available for their
+ // initialization.
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowRuleService flowRuleService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected GroupService groupService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowObjectiveStore flowObjectiveStore;
+
+ // Note: This must remain an optional dependency to allow re-install of default drivers.
+ // Note: For now disabled until we can move to OPTIONAL_UNARY dependency
+ // @Reference(cardinality = ReferenceCardinality.OPTIONAL_UNARY, policy = ReferencePolicy.DYNAMIC)
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DefaultDriverProviderService defaultDriverService;
+
+ private final FlowObjectiveStoreDelegate delegate = new InternalStoreDelegate();
+
+ private final Map<DeviceId, DriverHandler> driverHandlers = Maps.newConcurrentMap();
+ private final Map<DeviceId, Pipeliner> pipeliners = Maps.newConcurrentMap();
+
+ private final PipelinerContext context = new InnerPipelineContext();
+ private final MastershipListener mastershipListener = new InnerMastershipListener();
+ private final DeviceListener deviceListener = new InnerDeviceListener();
+
+ protected ServiceDirectory serviceDirectory = new DefaultServiceDirectory();
+
+ private Map<Integer, Set<PendingNext>> pendingForwards = Maps.newConcurrentMap();
+
+ private ExecutorService executorService;
+
+ private String policy;
+ private Map<DeviceId, FlowObjectiveCompositionTree> deviceCompositionTreeMap;
+
+ @Activate
+ protected void activate() {
+ executorService = newFixedThreadPool(4, groupedThreads("onos/objective-installer", "%d"));
+ flowObjectiveStore.setDelegate(delegate);
+ mastershipService.addListener(mastershipListener);
+ deviceService.addListener(deviceListener);
+ deviceService.getDevices().forEach(device -> setupPipelineHandler(device.id()));
+ deviceCompositionTreeMap = Maps.newConcurrentMap();
+ log.info("Started");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ flowObjectiveStore.unsetDelegate(delegate);
+ mastershipService.removeListener(mastershipListener);
+ deviceService.removeListener(deviceListener);
+ executorService.shutdown();
+ pipeliners.clear();
+ driverHandlers.clear();
+ deviceCompositionTreeMap.clear();
+ log.info("Stopped");
+ }
+
+ /**
+ * Task that passes the flow objective down to the driver. The task will
+ * make a few attempts to find the appropriate driver, then eventually give
+ * up and report an error if no suitable driver could be found.
+ */
+ private class ObjectiveInstaller implements Runnable {
+ private final DeviceId deviceId;
+ private final Objective objective;
+
+ private final int numAttempts;
+
+ public ObjectiveInstaller(DeviceId deviceId, Objective objective) {
+ this(deviceId, objective, 1);
+ }
+
+ public ObjectiveInstaller(DeviceId deviceId, Objective objective, int attemps) {
+ this.deviceId = checkNotNull(deviceId);
+ this.objective = checkNotNull(objective);
+ this.numAttempts = checkNotNull(attemps);
+ }
+
+ @Override
+ public void run() {
+ try {
+ Pipeliner pipeliner = getDevicePipeliner(deviceId);
+
+ if (pipeliner != null) {
+ if (objective instanceof NextObjective) {
+ pipeliner.next((NextObjective) objective);
+ } else if (objective instanceof ForwardingObjective) {
+ pipeliner.forward((ForwardingObjective) objective);
+ } else {
+ pipeliner.filter((FilteringObjective) objective);
+ }
+ } else if (numAttempts < INSTALL_RETRY_ATTEMPTS) {
+ Thread.sleep(INSTALL_RETRY_INTERVAL);
+ executorService.submit(new ObjectiveInstaller(deviceId, objective, numAttempts + 1));
+ } else {
+ // Otherwise we've tried a few times and failed, report an
+ // error back to the user.
+ objective.context().ifPresent(
+ c -> c.onError(objective, ObjectiveError.DEVICEMISSING));
+ }
+ } catch (Exception e) {
+ log.warn("Exception while installing flow objective", e);
+ }
+ }
+ }
+
+ @Override
+ public void filter(DeviceId deviceId, FilteringObjective filteringObjective) {
+ checkPermission(FLOWRULE_WRITE);
+
+ List<FilteringObjective> filteringObjectives
+ = this.deviceCompositionTreeMap.get(deviceId).updateFilter(filteringObjective);
+ for (FilteringObjective tmp : filteringObjectives) {
+ executorService.submit(new ObjectiveInstaller(deviceId, tmp));
+ }
+ }
+
+ @Override
+ public void forward(DeviceId deviceId, ForwardingObjective forwardingObjective) {
+ checkPermission(FLOWRULE_WRITE);
+
+ if (queueObjective(deviceId, forwardingObjective)) {
+ return;
+ }
+ List<ForwardingObjective> forwardingObjectives
+ = this.deviceCompositionTreeMap.get(deviceId).updateForward(forwardingObjective);
+ for (ForwardingObjective tmp : forwardingObjectives) {
+ executorService.submit(new ObjectiveInstaller(deviceId, tmp));
+ }
+ }
+
+ @Override
+ public void next(DeviceId deviceId, NextObjective nextObjective) {
+ checkPermission(FLOWRULE_WRITE);
+
+ List<NextObjective> nextObjectives = this.deviceCompositionTreeMap.get(deviceId).updateNext(nextObjective);
+ for (NextObjective tmp : nextObjectives) {
+ executorService.submit(new ObjectiveInstaller(deviceId, tmp));
+ }
+ }
+
+ @Override
+ public int allocateNextId() {
+ checkPermission(FLOWRULE_WRITE);
+
+ return flowObjectiveStore.allocateNextId();
+ }
+
+ private boolean queueObjective(DeviceId deviceId, ForwardingObjective fwd) {
+ if (fwd.nextId() != null &&
+ flowObjectiveStore.getNextGroup(fwd.nextId()) == null) {
+ log.trace("Queuing forwarding objective for nextId {}", fwd.nextId());
+ if (pendingForwards.putIfAbsent(fwd.nextId(),
+ Sets.newHashSet(new PendingNext(deviceId, fwd))) != null) {
+ Set<PendingNext> pending = pendingForwards.get(fwd.nextId());
+ pending.add(new PendingNext(deviceId, fwd));
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void initPolicy(String policy) {
+ this.policy = policy;
+ deviceService.getDevices().forEach(device ->
+ this.deviceCompositionTreeMap.put(device.id(), FlowObjectiveCompositionUtil.parsePolicyString(policy)));
+ log.info("Initialize policy {}", policy);
+ }
+
+ // Retrieves the device pipeline behaviour from the cache.
+ private Pipeliner getDevicePipeliner(DeviceId deviceId) {
+ Pipeliner pipeliner = pipeliners.get(deviceId);
+ return pipeliner;
+ }
+
+ private void setupPipelineHandler(DeviceId deviceId) {
+ if (defaultDriverService == null) {
+ // We're not ready to go to work yet.
+ return;
+ }
+
+ // Attempt to lookup the handler in the cache
+ DriverHandler handler = driverHandlers.get(deviceId);
+ if (handler == null) {
+ try {
+ // Otherwise create it and if it has pipeline behaviour, cache it
+ handler = driverService.createHandler(deviceId);
+ if (!handler.driver().hasBehaviour(Pipeliner.class)) {
+ log.warn("Pipeline behaviour not supported for device {}",
+ deviceId);
+ return;
+ }
+ } catch (ItemNotFoundException e) {
+ log.warn("No applicable driver for device {}", deviceId);
+ return;
+ }
+
+ driverHandlers.put(deviceId, handler);
+ }
+
+ // Always (re)initialize the pipeline behaviour
+ log.info("Driver {} bound to device {} ... initializing driver",
+ handler.driver().name(), deviceId);
+ Pipeliner pipeliner = handler.behaviour(Pipeliner.class);
+ pipeliner.init(deviceId, context);
+ pipeliners.putIfAbsent(deviceId, pipeliner);
+ }
+
+ // Triggers driver setup when the local node becomes a device master.
+ private class InnerMastershipListener implements MastershipListener {
+ @Override
+ public void event(MastershipEvent event) {
+ switch (event.type()) {
+ case MASTER_CHANGED:
+ log.debug("mastership changed on device {}", event.subject());
+ if (deviceService.isAvailable(event.subject())) {
+ setupPipelineHandler(event.subject());
+ }
+ break;
+ case BACKUPS_CHANGED:
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ // Triggers driver setup when a device is (re)detected.
+ private class InnerDeviceListener implements DeviceListener {
+ @Override
+ public void event(DeviceEvent event) {
+ switch (event.type()) {
+ case DEVICE_ADDED:
+ case DEVICE_AVAILABILITY_CHANGED:
+ log.debug("Device either added or availability changed {}",
+ event.subject().id());
+ if (deviceService.isAvailable(event.subject().id())) {
+ log.debug("Device is now available {}", event.subject().id());
+ setupPipelineHandler(event.subject().id());
+ }
+ break;
+ case DEVICE_UPDATED:
+ break;
+ case DEVICE_REMOVED:
+ break;
+ case DEVICE_SUSPENDED:
+ break;
+ case PORT_ADDED:
+ break;
+ case PORT_UPDATED:
+ break;
+ case PORT_REMOVED:
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ // Processing context for initializing pipeline driver behaviours.
+ private class InnerPipelineContext implements PipelinerContext {
+ @Override
+ public ServiceDirectory directory() {
+ return serviceDirectory;
+ }
+
+ @Override
+ public FlowObjectiveStore store() {
+ return flowObjectiveStore;
+ }
+ }
+
+ private class InternalStoreDelegate implements FlowObjectiveStoreDelegate {
+ @Override
+ public void notify(ObjectiveEvent event) {
+ log.debug("Received notification of obj event {}", event);
+ Set<PendingNext> pending = pendingForwards.remove(event.subject());
+
+ if (pending == null) {
+ log.debug("Nothing pending for this obj event");
+ return;
+ }
+
+ log.debug("Processing pending forwarding objectives {}", pending.size());
+
+ pending.forEach(p -> getDevicePipeliner(p.deviceId())
+ .forward(p.forwardingObjective()));
+
+ }
+ }
+
+ /**
+ * Data class used to hold a pending forwarding objective that could not
+ * be processed because the associated next object was not present.
+ */
+ private class PendingNext {
+ private final DeviceId deviceId;
+ private final ForwardingObjective fwd;
+
+ public PendingNext(DeviceId deviceId, ForwardingObjective fwd) {
+ this.deviceId = deviceId;
+ this.fwd = fwd;
+ }
+
+ public DeviceId deviceId() {
+ return deviceId;
+ }
+
+ public ForwardingObjective forwardingObjective() {
+ return fwd;
+ }
+ }
+
+ public static String forwardingObjectiveToString(ForwardingObjective forwardingObjective) {
+ String str = forwardingObjective.priority() + " ";
+ str += "selector( ";
+ for (Criterion criterion : forwardingObjective.selector().criteria()) {
+ str += criterion + " ";
+ }
+ str += ") treatment( ";
+ for (Instruction instruction : forwardingObjective.treatment().allInstructions()) {
+ str += instruction + " ";
+ }
+ str += ")";
+ return str;
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionTree.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionTree.java
new file mode 100644
index 00000000..152622b2
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionTree.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.flowobjective.impl.composition;
+
+import org.onosproject.net.flowobjective.FilteringObjective;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flowobjective.NextObjective;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Provides a policy tree to store all flow tables for each device.
+ *
+ * Note: This class uses in-memory structures and is not yet distributed.
+ */
+public class FlowObjectiveCompositionTree {
+
+ public FlowObjectiveCompositionManager.PolicyOperator operator;
+ public FlowObjectiveCompositionTree leftChild;
+ public FlowObjectiveCompositionTree rightChild;
+ public short applicationId;
+ protected FilterTable filterTable;
+ protected ForwardTable forwardTable;
+ protected NextTable nextTable;
+
+ protected int priorityMultiplier;
+ protected int priorityAddend;
+
+ public FlowObjectiveCompositionTree(short applicationId) {
+ this.operator = FlowObjectiveCompositionManager.PolicyOperator.Application;
+ this.leftChild = null;
+ this.rightChild = null;
+ this.applicationId = applicationId;
+ this.filterTable = new FilterTable();
+ this.forwardTable = new ForwardTable();
+ this.nextTable = new NextTable();
+ this.priorityMultiplier = 10;
+ this.priorityAddend = 10;
+ }
+
+ public FlowObjectiveCompositionTree(Character ch) {
+ switch (ch) {
+ case '+':
+ this.operator = FlowObjectiveCompositionManager.PolicyOperator.Parallel;
+ break;
+ case '>':
+ this.operator = FlowObjectiveCompositionManager.PolicyOperator.Sequential;
+ break;
+ case '/':
+ this.operator = FlowObjectiveCompositionManager.PolicyOperator.Override;
+ break;
+ default:
+ this.operator = FlowObjectiveCompositionManager.PolicyOperator.Application;
+ break;
+ }
+ this.leftChild = null;
+ this.rightChild = null;
+ this.applicationId = (short) -1;
+ this.filterTable = new FilterTable();
+ this.forwardTable = new ForwardTable();
+ this.nextTable = new NextTable();
+ this.priorityMultiplier = 10;
+ this.priorityAddend = 10;
+ }
+
+ protected List<FilteringObjective> updateFilter(FilteringObjective filteringObjective) {
+ switch (this.operator) {
+ case Parallel:
+ return updateFilterParallel(filteringObjective);
+ case Sequential:
+ return updateFilterSequential(filteringObjective);
+ case Override:
+ return updateFilterOverride(filteringObjective);
+ case Application:
+ if (filteringObjective.appId().id() == this.applicationId) {
+ return this.filterTable.updateFilter(filteringObjective);
+ } else {
+ return new ArrayList<>();
+ }
+ default:
+ return new ArrayList<>();
+ }
+ }
+
+ // Parallel composition: the filter set is the union of the children
+ protected List<FilteringObjective> updateFilterParallel(FilteringObjective filteringObjective) {
+ List<FilteringObjective> leftUpdates = this.leftChild.updateFilter(filteringObjective);
+ List<FilteringObjective> rightUpdates = this.rightChild.updateFilter(filteringObjective);
+
+ List<FilteringObjective> updates = new ArrayList<>();
+ updates.addAll(leftUpdates);
+ updates.addAll(rightUpdates);
+
+ return this.filterTable.updateFilter(updates);
+ }
+
+ // Sequential composition: the filter set is the filter set of the left child
+ protected List<FilteringObjective> updateFilterSequential(FilteringObjective filteringObjective) {
+ List<FilteringObjective> leftUpdates = this.leftChild.updateFilter(filteringObjective);
+ List<FilteringObjective> rightUpdates = this.rightChild.updateFilter(filteringObjective);
+ return this.filterTable.updateFilter(leftUpdates);
+ }
+
+ // Override composition: the filter set is the filter set of the left child
+ protected List<FilteringObjective> updateFilterOverride(FilteringObjective filteringObjective) {
+ List<FilteringObjective> leftUpdates = this.leftChild.updateFilter(filteringObjective);
+ List<FilteringObjective> rightUpdates = this.rightChild.updateFilter(filteringObjective);
+ return this.filterTable.updateFilter(leftUpdates);
+ }
+
+ public List<ForwardingObjective> updateForward(ForwardingObjective forwardingObjective) {
+ return this.updateForwardNode(forwardingObjective).toForwardingObjectiveList();
+ }
+
+ public ForwardUpdateTable updateForwardNode(ForwardingObjective forwardingObjective) {
+ switch (this.operator) {
+ case Parallel:
+ case Sequential:
+ case Override:
+ return updateForwardComposition(forwardingObjective);
+ case Application:
+ if (forwardingObjective.appId().id() == this.applicationId) {
+ return this.forwardTable.updateForward(forwardingObjective);
+ } else {
+ return (new ForwardUpdateTable());
+ }
+ default:
+ return (new ForwardUpdateTable());
+ }
+ }
+
+ protected ForwardUpdateTable updateForwardComposition(ForwardingObjective forwardingObjective) {
+ ForwardUpdateTable leftUpdates = this.leftChild.updateForwardNode(forwardingObjective);
+ ForwardUpdateTable rightUpdates = this.rightChild.updateForwardNode(forwardingObjective);
+
+ List<ForwardingObjective> addUpdates = new ArrayList<>();
+ List<ForwardingObjective> removeUpdates = new ArrayList<>();
+ // Handle ADD
+ if (this.operator == FlowObjectiveCompositionManager.PolicyOperator.Parallel
+ || this.operator == FlowObjectiveCompositionManager.PolicyOperator.Sequential) {
+ for (ForwardingObjective fo1 : leftUpdates.addObjectives) {
+ for (ForwardingObjective fo2 : this.rightChild.forwardTable.getForwardingObjectives()) {
+ ForwardingObjective composedFo = null;
+ if (this.operator == FlowObjectiveCompositionManager.PolicyOperator.Parallel) {
+ composedFo = FlowObjectiveCompositionUtil.composeParallel(fo1, fo2);
+ } else {
+ composedFo = FlowObjectiveCompositionUtil.composeSequential(fo1, fo2, this.priorityMultiplier);
+ }
+ if (composedFo != null) {
+ addUpdates.add(composedFo);
+ this.leftChild.forwardTable.addGeneratedParentForwardingObjective(fo1, composedFo);
+ this.rightChild.forwardTable.addGeneratedParentForwardingObjective(fo2, composedFo);
+ }
+ }
+ }
+ Collection<ForwardingObjective> leftTableWithoutAdd = FlowObjectiveCompositionUtil
+ .minusForwardingObjectives(this.leftChild.forwardTable.getForwardingObjectives(),
+ leftUpdates.addObjectives);
+ for (ForwardingObjective fo1 : leftTableWithoutAdd) {
+ for (ForwardingObjective fo2 : rightUpdates.addObjectives) {
+ ForwardingObjective composedFo = null;
+ if (this.operator == FlowObjectiveCompositionManager.PolicyOperator.Parallel) {
+ composedFo = FlowObjectiveCompositionUtil.composeParallel(fo1, fo2);
+ } else {
+ composedFo = FlowObjectiveCompositionUtil.composeSequential(fo1, fo2, this.priorityMultiplier);
+ }
+ if (composedFo != null) {
+ addUpdates.add(composedFo);
+ this.leftChild.forwardTable.addGeneratedParentForwardingObjective(fo1, composedFo);
+ this.rightChild.forwardTable.addGeneratedParentForwardingObjective(fo2, composedFo);
+ }
+ }
+ }
+ } else {
+ for (ForwardingObjective fo : leftUpdates.addObjectives) {
+ ForwardingObjective composedFo = FlowObjectiveCompositionUtil.composeOverride(fo, this.priorityAddend);
+ addUpdates.add(composedFo);
+ this.leftChild.forwardTable.addGeneratedParentForwardingObjective(fo, composedFo);
+ }
+ for (ForwardingObjective fo : rightUpdates.addObjectives) {
+ ForwardingObjective composedFo = FlowObjectiveCompositionUtil.composeOverride(fo, 0);
+ addUpdates.add(composedFo);
+ this.rightChild.forwardTable.addGeneratedParentForwardingObjective(fo, composedFo);
+ }
+ }
+
+ // Handle REMOVE
+ for (ForwardingObjective fo : leftUpdates.removeObjectives) {
+ List<ForwardingObjective> fos = this.leftChild.forwardTable
+ .getGeneratedParentForwardingObjectiveForRemove(fo);
+ removeUpdates.addAll(fos);
+ }
+ this.leftChild.forwardTable.deleteGeneratedParentForwardingObjective(leftUpdates.removeObjectives);
+ for (ForwardingObjective fo : rightUpdates.removeObjectives) {
+ List<ForwardingObjective> fos = this.rightChild.forwardTable
+ .getGeneratedParentForwardingObjectiveForRemove(fo);
+ removeUpdates.addAll(fos);
+ }
+ this.rightChild.forwardTable.deleteGeneratedParentForwardingObjective(rightUpdates.removeObjectives);
+
+ ForwardUpdateTable updates = new ForwardUpdateTable();
+ updates.addUpdateTable(this.forwardTable.updateForward(addUpdates));
+ updates.addUpdateTable(this.forwardTable.updateForward(removeUpdates));
+ return updates;
+ }
+
+ public List<NextObjective> updateNext(NextObjective nextObjective) {
+ switch (this.operator) {
+ case Parallel:
+ case Sequential:
+ case Override:
+ return updateNextComposition(nextObjective);
+ case Application:
+ if (nextObjective.appId().id() == this.applicationId) {
+ return this.nextTable.updateNext(nextObjective);
+ } else {
+ return new ArrayList<>();
+ }
+ default:
+ return new ArrayList<>();
+ }
+ }
+
+ // Next: the union of the children
+ protected List<NextObjective> updateNextComposition(NextObjective nextObjective) {
+ List<NextObjective> leftUpdates = this.leftChild.updateNext(nextObjective);
+ List<NextObjective> rightUpdates = this.rightChild.updateNext(nextObjective);
+
+ List<NextObjective> updates = new ArrayList<>();
+ updates.addAll(leftUpdates);
+ updates.addAll(rightUpdates);
+
+ return this.nextTable.updateNext(updates);
+ }
+
+ @Override
+ public String toString() {
+ String str = null;
+ switch (this.operator) {
+ case Parallel:
+ str = "(" + this.leftChild + "+" + this.rightChild + ")";
+ break;
+ case Sequential:
+ str = "(" + this.leftChild + ">" + this.rightChild + ")";
+ break;
+ case Override:
+ str = "(" + this.leftChild + "/" + this.rightChild + ")";
+ break;
+ default:
+ str = " " + applicationId + " ";
+ break;
+ }
+ return str;
+ }
+
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionUtil.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionUtil.java
new file mode 100644
index 00000000..137aca1e
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/FlowObjectiveCompositionUtil.java
@@ -0,0 +1,488 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.flowobjective.impl.composition;
+
+import org.onlab.packet.IpPrefix;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.LambdaCriterion;
+import org.onosproject.net.flow.criteria.OchSignalCriterion;
+import org.onosproject.net.flow.criteria.EthCriterion;
+import org.onosproject.net.flow.criteria.VlanIdCriterion;
+import org.onosproject.net.flow.criteria.VlanPcpCriterion;
+import org.onosproject.net.flow.criteria.MplsCriterion;
+import org.onosproject.net.flow.criteria.IPCriterion;
+import org.onosproject.net.flow.criteria.IPv6FlowLabelCriterion;
+import org.onosproject.net.flow.criteria.Criteria;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.L0ModificationInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction;
+import org.onosproject.net.flow.instructions.L3ModificationInstruction;
+import org.onosproject.net.flowobjective.DefaultForwardingObjective;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
+
+/**
+ * Provide util functions for FlowObjectiveComposition.
+ */
+public final class FlowObjectiveCompositionUtil {
+
+ private FlowObjectiveCompositionUtil() {}
+
+ // only work with VERSATILE
+ public static ForwardingObjective composeParallel(ForwardingObjective fo1, ForwardingObjective fo2) {
+
+ TrafficSelector trafficSelector = intersectTrafficSelector(fo1.selector(), fo2.selector());
+ if (trafficSelector == null) {
+ return null;
+ }
+
+ TrafficTreatment trafficTreatment = unionTrafficTreatment(fo1.treatment(), fo2.treatment());
+
+ return DefaultForwardingObjective.builder()
+ .fromApp(fo1.appId())
+ .makePermanent()
+ .withFlag(ForwardingObjective.Flag.VERSATILE)
+ .withPriority(fo1.priority() + fo2.priority())
+ .withSelector(trafficSelector)
+ .withTreatment(trafficTreatment)
+ .add();
+ }
+
+ public static ForwardingObjective composeSequential(ForwardingObjective fo1,
+ ForwardingObjective fo2,
+ int priorityMultiplier) {
+
+ TrafficSelector revertTrafficSelector = revertTreatmentSelector(fo1.treatment(), fo2.selector());
+ if (revertTrafficSelector == null) {
+ return null;
+ }
+
+ TrafficSelector trafficSelector = intersectTrafficSelector(fo1.selector(), revertTrafficSelector);
+ if (trafficSelector == null) {
+ return null;
+ }
+
+ TrafficTreatment trafficTreatment = unionTrafficTreatment(fo1.treatment(), fo2.treatment());
+
+ return DefaultForwardingObjective.builder()
+ .fromApp(fo1.appId())
+ .makePermanent()
+ .withFlag(ForwardingObjective.Flag.VERSATILE)
+ .withPriority(fo1.priority() * priorityMultiplier + fo2.priority())
+ .withSelector(trafficSelector)
+ .withTreatment(trafficTreatment)
+ .add();
+ }
+
+ public static ForwardingObjective composeOverride(ForwardingObjective fo, int priorityAddend) {
+ return DefaultForwardingObjective.builder()
+ .fromApp(fo.appId())
+ .makePermanent()
+ .withFlag(fo.flag())
+ .withPriority(fo.priority() + priorityAddend)
+ .withSelector(fo.selector())
+ .withTreatment(fo.treatment())
+ .add();
+ }
+
+ public static TrafficSelector intersectTrafficSelector(TrafficSelector ts1, TrafficSelector ts2) {
+
+ TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
+
+ Set<Criterion.Type> ts1IntersectTs2 = getTypeSet(ts1);
+ ts1IntersectTs2.retainAll(getTypeSet(ts2));
+ for (Criterion.Type type : ts1IntersectTs2) {
+ Criterion criterion = intersectCriterion(ts1.getCriterion(type), ts2.getCriterion(type));
+ if (criterion == null) {
+ return null;
+ } else {
+ selectorBuilder.add(criterion);
+ }
+ }
+
+ Set<Criterion.Type> ts1MinusTs2 = getTypeSet(ts1);
+ ts1MinusTs2.removeAll(getTypeSet(ts2));
+ for (Criterion.Type type : ts1MinusTs2) {
+ selectorBuilder.add(ts1.getCriterion(type));
+ }
+
+ Set<Criterion.Type> ts2MinusTs1 = getTypeSet(ts2);
+ ts2MinusTs1.removeAll(getTypeSet(ts1));
+ for (Criterion.Type type : ts2MinusTs1) {
+ selectorBuilder.add(ts2.getCriterion(type));
+ }
+
+ return selectorBuilder.build();
+ }
+
+ public static TrafficTreatment unionTrafficTreatment(TrafficTreatment tt1, TrafficTreatment tt2) {
+
+ TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
+
+ for (Instruction instruction : tt1.allInstructions()) {
+ treatmentBuilder.add(instruction);
+ }
+
+ for (Instruction instruction : tt2.allInstructions()) {
+ treatmentBuilder.add(instruction);
+ }
+
+ return treatmentBuilder.build();
+ }
+
+ public static TrafficSelector revertTreatmentSelector(TrafficTreatment trafficTreatment,
+ TrafficSelector trafficSelector) {
+
+ TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
+
+ Map<Criterion.Type, Criterion> criterionMap = new HashMap<>();
+ for (Criterion criterion : trafficSelector.criteria()) {
+ criterionMap.put(criterion.type(), criterion);
+ }
+
+ for (Instruction instruction : trafficTreatment.allInstructions()) {
+ switch (instruction.type()) {
+ case DROP:
+ return null;
+ case OUTPUT:
+ break;
+ case GROUP:
+ break;
+ case L0MODIFICATION: {
+ L0ModificationInstruction l0 = (L0ModificationInstruction) instruction;
+ switch (l0.subtype()) {
+ case LAMBDA:
+ if (criterionMap.containsKey(Criterion.Type.OCH_SIGID)) {
+ if (((LambdaCriterion) criterionMap.get((Criterion.Type.OCH_SIGID))).lambda()
+ == ((L0ModificationInstruction.ModLambdaInstruction) l0).lambda()) {
+ criterionMap.remove(Criterion.Type.OCH_SIGID);
+ } else {
+ return null;
+ }
+ } else {
+ break;
+ }
+ case OCH:
+ if (criterionMap.containsKey(Criterion.Type.OCH_SIGID)) {
+ if (((OchSignalCriterion) criterionMap.get((Criterion.Type.OCH_SIGID))).lambda()
+ .equals(((L0ModificationInstruction.ModOchSignalInstruction) l0).lambda())) {
+ criterionMap.remove(Criterion.Type.OCH_SIGID);
+ } else {
+ return null;
+ }
+ } else {
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ case L2MODIFICATION: {
+ L2ModificationInstruction l2 = (L2ModificationInstruction) instruction;
+ switch (l2.subtype()) {
+ case ETH_SRC:
+ if (criterionMap.containsKey(Criterion.Type.ETH_SRC)) {
+ if (((EthCriterion) criterionMap.get((Criterion.Type.ETH_SRC))).mac()
+ .equals(((L2ModificationInstruction.ModEtherInstruction) l2).mac())) {
+ criterionMap.remove(Criterion.Type.ETH_SRC);
+ } else {
+ return null;
+ }
+ } else {
+ break;
+ }
+ case ETH_DST:
+ if (criterionMap.containsKey(Criterion.Type.ETH_DST)) {
+ if (((EthCriterion) criterionMap.get((Criterion.Type.ETH_DST))).mac()
+ .equals(((L2ModificationInstruction.ModEtherInstruction) l2).mac())) {
+ criterionMap.remove(Criterion.Type.ETH_DST);
+ } else {
+ return null;
+ }
+ } else {
+ break;
+ }
+ case VLAN_ID:
+ if (criterionMap.containsKey(Criterion.Type.VLAN_VID)) {
+ if (((VlanIdCriterion) criterionMap.get((Criterion.Type.VLAN_VID))).vlanId()
+ .equals(((L2ModificationInstruction.ModVlanIdInstruction) l2).vlanId())) {
+ criterionMap.remove(Criterion.Type.VLAN_VID);
+ } else {
+ return null;
+ }
+ } else {
+ break;
+ }
+ case VLAN_PCP:
+ if (criterionMap.containsKey(Criterion.Type.VLAN_PCP)) {
+ if (((VlanPcpCriterion) criterionMap.get((Criterion.Type.VLAN_PCP))).priority()
+ == ((L2ModificationInstruction.ModVlanPcpInstruction) l2).vlanPcp()) {
+ criterionMap.remove(Criterion.Type.VLAN_PCP);
+ } else {
+ return null;
+ }
+ } else {
+ break;
+ }
+ case MPLS_LABEL:
+ if (criterionMap.containsKey(Criterion.Type.MPLS_LABEL)) {
+ if (((MplsCriterion) criterionMap.get((Criterion.Type.MPLS_LABEL))).label()
+ .equals(((L2ModificationInstruction.ModMplsLabelInstruction) l2).mplsLabel())) {
+ criterionMap.remove(Criterion.Type.ETH_DST);
+ } else {
+ return null;
+ }
+ } else {
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ case TABLE:
+ break;
+ case L3MODIFICATION: {
+ L3ModificationInstruction l3 = (L3ModificationInstruction) instruction;
+ switch (l3.subtype()) {
+ case IPV4_SRC:
+ if (criterionMap.containsKey(Criterion.Type.IPV4_SRC)) {
+ if (((IPCriterion) criterionMap.get(Criterion.Type.IPV4_SRC)).ip()
+ .contains(((L3ModificationInstruction.ModIPInstruction) l3).ip())) {
+ criterionMap.remove(Criterion.Type.IPV4_SRC);
+ } else {
+ return null;
+ }
+ } else {
+ break;
+ }
+ case IPV4_DST:
+ if (criterionMap.containsKey(Criterion.Type.IPV4_DST)) {
+ if (((IPCriterion) criterionMap.get(Criterion.Type.IPV4_DST)).ip()
+ .contains(((L3ModificationInstruction.ModIPInstruction) l3).ip())) {
+ criterionMap.remove(Criterion.Type.IPV4_DST);
+ } else {
+ return null;
+ }
+ } else {
+ break;
+ }
+ case IPV6_SRC:
+ if (criterionMap.containsKey(Criterion.Type.IPV6_SRC)) {
+ if (((IPCriterion) criterionMap.get(Criterion.Type.IPV6_SRC)).ip()
+ .contains(((L3ModificationInstruction.ModIPInstruction) l3).ip())) {
+ criterionMap.remove(Criterion.Type.IPV6_SRC);
+ } else {
+ return null;
+ }
+ } else {
+ break;
+ }
+ case IPV6_DST:
+ if (criterionMap.containsKey(Criterion.Type.IPV6_DST)) {
+ if (((IPCriterion) criterionMap.get(Criterion.Type.IPV6_DST)).ip()
+ .contains(((L3ModificationInstruction.ModIPInstruction) l3).ip())) {
+ criterionMap.remove(Criterion.Type.IPV6_DST);
+ } else {
+ return null;
+ }
+ } else {
+ break;
+ }
+ case IPV6_FLABEL:
+ if (criterionMap.containsKey(Criterion.Type.IPV6_FLABEL)) {
+ if (((IPv6FlowLabelCriterion) criterionMap.get(Criterion.Type.IPV6_FLABEL)).flowLabel()
+ == (((L3ModificationInstruction.ModIPv6FlowLabelInstruction) l3).flowLabel())) {
+ criterionMap.remove(Criterion.Type.IPV4_SRC);
+ } else {
+ return null;
+ }
+ } else {
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ case METADATA:
+ break;
+ default:
+ break;
+ }
+ }
+
+ for (Criterion criterion : criterionMap.values()) {
+ selectorBuilder.add(criterion);
+ }
+
+ return selectorBuilder.build();
+ }
+
+ public static Set<Criterion.Type> getTypeSet(TrafficSelector trafficSelector) {
+ Set<Criterion.Type> typeSet = new HashSet<>();
+ for (Criterion criterion : trafficSelector.criteria()) {
+ typeSet.add(criterion.type());
+ }
+ return typeSet;
+ }
+
+ public static Criterion intersectCriterion(Criterion c1, Criterion c2) {
+ switch (c1.type()) {
+ case IPV4_SRC: {
+ IpPrefix ipPrefix = intersectIpPrefix(((IPCriterion) c1).ip(), ((IPCriterion) c2).ip());
+ if (ipPrefix == null) {
+ return null;
+ } else {
+ return Criteria.matchIPSrc(ipPrefix);
+ }
+ }
+ case IPV4_DST: {
+ IpPrefix ipPrefix = intersectIpPrefix(((IPCriterion) c1).ip(), ((IPCriterion) c2).ip());
+ if (ipPrefix == null) {
+ return null;
+ } else {
+ return Criteria.matchIPDst(ipPrefix);
+ }
+ }
+ case IPV6_SRC: {
+ IpPrefix ipPrefix = intersectIpPrefix(((IPCriterion) c1).ip(), ((IPCriterion) c2).ip());
+ if (ipPrefix == null) {
+ return null;
+ } else {
+ return Criteria.matchIPv6Src(ipPrefix);
+ }
+ }
+ case IPV6_DST: {
+ IpPrefix ipPrefix = intersectIpPrefix(((IPCriterion) c1).ip(), ((IPCriterion) c2).ip());
+ if (ipPrefix == null) {
+ return null;
+ } else {
+ return Criteria.matchIPv6Dst(ipPrefix);
+ }
+ }
+ default:
+ if (!c1.equals(c2)) {
+ return null;
+ } else {
+ return c1;
+ }
+ }
+ }
+
+ public static IpPrefix intersectIpPrefix(IpPrefix ip1, IpPrefix ip2) {
+ if (ip1.contains(ip2)) {
+ return ip1;
+ } else if (ip2.contains(ip1)) {
+ return ip2;
+ } else {
+ return null;
+ }
+ }
+
+ public static FlowObjectiveCompositionTree parsePolicyString(String policy) {
+ List<FlowObjectiveCompositionTree> postfix = transformToPostfixForm(policy);
+ return buildPolicyTree(postfix);
+ }
+
+ private static List<FlowObjectiveCompositionTree> transformToPostfixForm(String policy) {
+ Stack<Character> stack = new Stack<>();
+ List<FlowObjectiveCompositionTree> postfix = new ArrayList<>();
+
+ for (int i = 0; i < policy.length(); i++) {
+ Character ch = policy.charAt(i);
+ if (Character.isDigit(ch)) {
+
+ int applicationId = ch - '0';
+ while (i + 1 < policy.length() && Character.isDigit(policy.charAt(i + 1))) {
+ i++;
+ applicationId = applicationId * 10 + policy.charAt(i) - '0';
+ }
+
+ postfix.add(new FlowObjectiveCompositionTree((short) applicationId));
+ } else if (ch == '(') {
+ stack.push(ch);
+ } else if (ch == ')') {
+ while (stack.peek() != '(') {
+ postfix.add(new FlowObjectiveCompositionTree(stack.pop()));
+ }
+ stack.pop();
+ } else {
+ while (!stack.isEmpty() && compareOperatorPriority(stack.peek(), ch)) {
+ postfix.add(new FlowObjectiveCompositionTree(stack.pop()));
+ }
+ stack.push(ch);
+ }
+ }
+ while (!stack.isEmpty()) {
+ postfix.add(new FlowObjectiveCompositionTree(stack.pop()));
+ }
+
+ return postfix;
+ }
+
+ private static boolean compareOperatorPriority(char peek, char cur) {
+ if (peek == '/' && (cur == '+' || cur == '>' || cur == '/')) {
+ return true;
+ } else if (peek == '>' && (cur == '+' || cur == '>')) {
+ return true;
+ } else if (peek == '+' && cur == '+') {
+ return true;
+ }
+ return false;
+ }
+
+ private static FlowObjectiveCompositionTree buildPolicyTree(List<FlowObjectiveCompositionTree> postfix) {
+ Stack<FlowObjectiveCompositionTree> stack = new Stack<>();
+ for (FlowObjectiveCompositionTree node : postfix) {
+ if (node.operator == FlowObjectiveCompositionManager.PolicyOperator.Application) {
+ stack.push(node);
+ } else {
+ node.rightChild = stack.pop();
+ node.leftChild = stack.pop();
+ stack.push(node);
+ }
+ }
+ return stack.pop();
+ }
+
+ public static Collection<ForwardingObjective> minusForwardingObjectives(Collection<ForwardingObjective> fo1,
+ Collection<ForwardingObjective> fo2) {
+ Map<Integer, ForwardingObjective> map = new HashMap<>();
+ for (ForwardingObjective fo : fo1) {
+ map.put(fo.id(), fo);
+ }
+ for (ForwardingObjective fo : fo2) {
+ map.remove(fo.id());
+ }
+ return map.values();
+ }
+
+
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/ForwardTable.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/ForwardTable.java
new file mode 100644
index 00000000..1384bbe2
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/ForwardTable.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.flowobjective.impl.composition;
+
+import org.onosproject.net.flowobjective.DefaultForwardingObjective;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Provides a table to store Forward.
+ */
+public class ForwardTable {
+
+ protected Map<Integer, ForwardingObjective> forwardMap;
+ protected Map<Integer, List<ForwardingObjective>> generatedParentForwardingObjectiveMap;
+
+ public ForwardTable() {
+ this.forwardMap = new HashMap<>();
+ this.generatedParentForwardingObjectiveMap = new HashMap<>();
+ }
+
+ public ForwardUpdateTable updateForward(ForwardingObjective forwardingObjective) {
+ ForwardUpdateTable updates = new ForwardUpdateTable();
+ switch (forwardingObjective.op()) {
+ case ADD:
+ this.forwardMap.put(forwardingObjectiveHash(forwardingObjective), forwardingObjective);
+ this.generatedParentForwardingObjectiveMap
+ .put(forwardingObjectiveHash(forwardingObjective), new ArrayList<>());
+ updates.addObjectives.add(forwardingObjective);
+ break;
+ case REMOVE:
+ if (this.forwardMap.remove(forwardingObjectiveHash(forwardingObjective)) != null) {
+ updates.removeObjectives.add(forwardingObjective);
+ }
+ break;
+ default:
+ break;
+ }
+ return updates;
+ }
+
+ public ForwardUpdateTable updateForward(List<ForwardingObjective> forwardingObjectives) {
+ ForwardUpdateTable updates = new ForwardUpdateTable();
+ for (ForwardingObjective forwardingObjective : forwardingObjectives) {
+ updates.addUpdateTable(this.updateForward(forwardingObjective));
+ }
+ return updates;
+ }
+
+ public void addGeneratedParentForwardingObjective(ForwardingObjective child, ForwardingObjective parent) {
+ this.generatedParentForwardingObjectiveMap.get(forwardingObjectiveHash(child)).add(parent);
+ }
+
+ public void deleteGeneratedParentForwardingObjective(List<ForwardingObjective> children) {
+ for (ForwardingObjective fo : children) {
+ this.generatedParentForwardingObjectiveMap.remove(forwardingObjectiveHash(fo));
+ }
+ }
+
+ private List<ForwardingObjective> getGeneratedParentForwardingObjective(ForwardingObjective child) {
+ return this.generatedParentForwardingObjectiveMap.get(forwardingObjectiveHash(child));
+ }
+
+ public List<ForwardingObjective> getGeneratedParentForwardingObjectiveForRemove(ForwardingObjective child) {
+ List<ForwardingObjective> fos = this.generatedParentForwardingObjectiveMap.get(forwardingObjectiveHash(child));
+ List<ForwardingObjective> removeFos = new ArrayList<>();
+ for (ForwardingObjective fo : fos) {
+ removeFos.add(DefaultForwardingObjective.builder()
+ .fromApp(fo.appId())
+ .makePermanent()
+ .withFlag(fo.flag())
+ .withPriority(fo.priority())
+ .withSelector(fo.selector())
+ .withTreatment(fo.treatment())
+ .remove());
+ }
+ return removeFos;
+ }
+
+ public Collection<ForwardingObjective> getForwardingObjectives() {
+ return this.forwardMap.values();
+ }
+
+ public static int forwardingObjectiveHash(ForwardingObjective forwardingObjective) {
+ return Objects.hash(forwardingObjective.selector(), forwardingObjective.flag(),
+ forwardingObjective.permanent(), forwardingObjective.timeout(),
+ forwardingObjective.appId(), forwardingObjective.priority(),
+ forwardingObjective.nextId(), forwardingObjective.treatment());
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/ForwardUpdateTable.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/ForwardUpdateTable.java
new file mode 100644
index 00000000..9818cfd5
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/ForwardUpdateTable.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.flowobjective.impl.composition;
+
+import org.onosproject.net.flowobjective.ForwardingObjective;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Provides an update table for Forward.
+ */
+public class ForwardUpdateTable {
+ public List<ForwardingObjective> addObjectives;
+ public List<ForwardingObjective> removeObjectives;
+
+ public ForwardUpdateTable() {
+ this.addObjectives = new ArrayList<>();
+ this.removeObjectives = new ArrayList<>();
+ }
+
+ public void addUpdateTable(ForwardUpdateTable updateTable) {
+ this.addObjectives.addAll(updateTable.addObjectives);
+ this.removeObjectives.addAll(updateTable.removeObjectives);
+ }
+
+ public List<ForwardingObjective> toForwardingObjectiveList() {
+ List<ForwardingObjective> forwardingObjectives = new ArrayList<>();
+ forwardingObjectives.addAll(this.addObjectives);
+ forwardingObjectives.addAll(this.removeObjectives);
+ return forwardingObjectives;
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/NextTable.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/NextTable.java
new file mode 100644
index 00000000..e2787edd
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/NextTable.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.flowobjective.impl.composition;
+
+import org.onosproject.net.flowobjective.NextObjective;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Provides a table to store Next.
+ */
+public class NextTable {
+
+ protected Map<Integer, NextObjective> nextMap;
+
+ public NextTable() {
+ this.nextMap = new HashMap<>();
+ }
+
+ public List<NextObjective> updateNext(NextObjective nextObjective) {
+ List<NextObjective> updates = new ArrayList<>();
+ switch (nextObjective.op()) {
+ case ADD:
+ this.nextMap.put(nextObjective.id(), nextObjective);
+ updates.add(nextObjective);
+ break;
+ case REMOVE:
+ this.nextMap.remove(nextObjective.id());
+ updates.add(nextObjective);
+ break;
+ default:
+ break;
+ }
+ return updates;
+ }
+
+ public List<NextObjective> updateNext(List<NextObjective> nextObjectives) {
+ List<NextObjective> updates = new ArrayList<>();
+ for (NextObjective nextObjective : nextObjectives) {
+ updates.addAll(this.updateNext(nextObjective));
+ }
+ return updates;
+ }
+
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/package-info.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/package-info.java
new file mode 100644
index 00000000..da2a9850
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/composition/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Prototype of a composition mechanism for flow objective composition.
+ */
+package org.onosproject.net.flowobjective.impl.composition; \ No newline at end of file
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/package-info.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/package-info.java
new file mode 100644
index 00000000..c0779dc2
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/flowobjective/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Implementations of the flow objective programming subsystem.
+ */
+package org.onosproject.net.flowobjective.impl; \ No newline at end of file
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/group/impl/GroupManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/group/impl/GroupManager.java
new file mode 100644
index 00000000..96e9b198
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/group/impl/GroupManager.java
@@ -0,0 +1,318 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.group.impl;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.net.provider.AbstractListenerProviderRegistry;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.group.GroupBuckets;
+import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.group.GroupEvent;
+import org.onosproject.net.group.GroupKey;
+import org.onosproject.net.group.GroupListener;
+import org.onosproject.net.group.GroupOperation;
+import org.onosproject.net.group.GroupOperations;
+import org.onosproject.net.group.GroupProvider;
+import org.onosproject.net.group.GroupProviderRegistry;
+import org.onosproject.net.group.GroupProviderService;
+import org.onosproject.net.group.GroupService;
+import org.onosproject.net.group.GroupStore;
+import org.onosproject.net.group.GroupStore.UpdateType;
+import org.onosproject.net.group.GroupStoreDelegate;
+import org.onosproject.net.provider.AbstractProviderService;
+import org.slf4j.Logger;
+
+import java.util.Collection;
+import java.util.Collections;
+
+import static org.onosproject.security.AppGuard.checkPermission;
+import static org.slf4j.LoggerFactory.getLogger;
+import static org.onosproject.security.AppPermission.Type.*;
+
+
+
+/**
+ * Provides implementation of the group service APIs.
+ */
+@Component(immediate = true)
+@Service
+public class GroupManager
+ extends AbstractListenerProviderRegistry<GroupEvent, GroupListener,
+ GroupProvider, GroupProviderService>
+ implements GroupService, GroupProviderRegistry {
+
+ private final Logger log = getLogger(getClass());
+
+ private final GroupStoreDelegate delegate = new InternalGroupStoreDelegate();
+ private final DeviceListener deviceListener = new InternalDeviceListener();
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected GroupStore store;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ @Activate
+ public void activate() {
+ store.setDelegate(delegate);
+ eventDispatcher.addSink(GroupEvent.class, listenerRegistry);
+ deviceService.addListener(deviceListener);
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ store.unsetDelegate(delegate);
+ eventDispatcher.removeSink(GroupEvent.class);
+ log.info("Stopped");
+ }
+
+ /**
+ * Create a group in the specified device with the provided parameters.
+ *
+ * @param groupDesc group creation parameters
+ */
+ @Override
+ public void addGroup(GroupDescription groupDesc) {
+ checkPermission(GROUP_WRITE);
+ store.storeGroupDescription(groupDesc);
+ }
+
+ /**
+ * Return a group object associated to an application cookie.
+ * <p>
+ * NOTE1: The presence of group object in the system does not
+ * guarantee that the "group" is actually created in device.
+ * GROUP_ADDED notification would confirm the creation of
+ * this group in data plane.
+ *
+ * @param deviceId device identifier
+ * @param appCookie application cookie to be used for lookup
+ * @return group associated with the application cookie or
+ * NULL if Group is not found for the provided cookie
+ */
+ @Override
+ public Group getGroup(DeviceId deviceId, GroupKey appCookie) {
+ checkPermission(GROUP_READ);
+ return store.getGroup(deviceId, appCookie);
+ }
+
+ /**
+ * Append buckets to existing group. The caller can optionally
+ * associate a new cookie during this updation. GROUP_UPDATED or
+ * GROUP_UPDATE_FAILED notifications would be provided along with
+ * cookie depending on the result of the operation on the device.
+ *
+ * @param deviceId device identifier
+ * @param oldCookie cookie to be used to retrieve the existing group
+ * @param buckets immutable list of group bucket to be added
+ * @param newCookie immutable cookie to be used post update operation
+ * @param appId Application Id
+ */
+ @Override
+ public void addBucketsToGroup(DeviceId deviceId,
+ GroupKey oldCookie,
+ GroupBuckets buckets,
+ GroupKey newCookie,
+ ApplicationId appId) {
+ checkPermission(GROUP_WRITE);
+ store.updateGroupDescription(deviceId,
+ oldCookie,
+ UpdateType.ADD,
+ buckets,
+ newCookie);
+ }
+
+ /**
+ * Remove buckets from existing group. The caller can optionally
+ * associate a new cookie during this updation. GROUP_UPDATED or
+ * GROUP_UPDATE_FAILED notifications would be provided along with
+ * cookie depending on the result of the operation on the device.
+ *
+ * @param deviceId device identifier
+ * @param oldCookie cookie to be used to retrieve the existing group
+ * @param buckets immutable list of group bucket to be removed
+ * @param newCookie immutable cookie to be used post update operation
+ * @param appId Application Id
+ */
+ @Override
+ public void removeBucketsFromGroup(DeviceId deviceId,
+ GroupKey oldCookie,
+ GroupBuckets buckets,
+ GroupKey newCookie,
+ ApplicationId appId) {
+ checkPermission(GROUP_WRITE);
+ store.updateGroupDescription(deviceId,
+ oldCookie,
+ UpdateType.REMOVE,
+ buckets,
+ newCookie);
+ }
+
+ /**
+ * Delete a group associated to an application cookie.
+ * GROUP_DELETED or GROUP_DELETE_FAILED notifications would be
+ * provided along with cookie depending on the result of the
+ * operation on the device.
+ *
+ * @param deviceId device identifier
+ * @param appCookie application cookie to be used for lookup
+ * @param appId Application Id
+ */
+ @Override
+ public void removeGroup(DeviceId deviceId,
+ GroupKey appCookie,
+ ApplicationId appId) {
+ checkPermission(GROUP_WRITE);
+ store.deleteGroupDescription(deviceId, appCookie);
+ }
+
+ /**
+ * Retrieve all groups created by an application in the specified device
+ * as seen by current controller instance.
+ *
+ * @param deviceId device identifier
+ * @param appId application id
+ * @return collection of immutable group objects created by the application
+ */
+ @Override
+ public Iterable<Group> getGroups(DeviceId deviceId,
+ ApplicationId appId) {
+ checkPermission(GROUP_READ);
+ return store.getGroups(deviceId);
+ }
+
+ @Override
+ public Iterable<Group> getGroups(DeviceId deviceId) {
+ checkPermission(GROUP_READ);
+ return store.getGroups(deviceId);
+ }
+
+ @Override
+ protected GroupProviderService createProviderService(GroupProvider provider) {
+ return new InternalGroupProviderService(provider);
+ }
+
+ private class InternalGroupStoreDelegate implements GroupStoreDelegate {
+ @Override
+ public void notify(GroupEvent event) {
+ final Group group = event.subject();
+ GroupProvider groupProvider =
+ getProvider(group.deviceId());
+ GroupOperations groupOps = null;
+ switch (event.type()) {
+ case GROUP_ADD_REQUESTED:
+ log.debug("GROUP_ADD_REQUESTED for Group {} on device {}",
+ group.id(), group.deviceId());
+ GroupOperation groupAddOp = GroupOperation.
+ createAddGroupOperation(group.id(),
+ group.type(),
+ group.buckets());
+ groupOps = new GroupOperations(
+ Collections.singletonList(groupAddOp));
+ groupProvider.performGroupOperation(group.deviceId(), groupOps);
+ break;
+
+ case GROUP_UPDATE_REQUESTED:
+ log.debug("GROUP_UPDATE_REQUESTED for Group {} on device {}",
+ group.id(), group.deviceId());
+ GroupOperation groupModifyOp = GroupOperation.
+ createModifyGroupOperation(group.id(),
+ group.type(),
+ group.buckets());
+ groupOps = new GroupOperations(
+ Collections.singletonList(groupModifyOp));
+ groupProvider.performGroupOperation(group.deviceId(), groupOps);
+ break;
+
+ case GROUP_REMOVE_REQUESTED:
+ log.debug("GROUP_REMOVE_REQUESTED for Group {} on device {}",
+ group.id(), group.deviceId());
+ GroupOperation groupDeleteOp = GroupOperation.
+ createDeleteGroupOperation(group.id(),
+ group.type());
+ groupOps = new GroupOperations(
+ Collections.singletonList(groupDeleteOp));
+ groupProvider.performGroupOperation(group.deviceId(), groupOps);
+ break;
+
+ case GROUP_ADDED:
+ case GROUP_UPDATED:
+ case GROUP_REMOVED:
+ case GROUP_ADD_FAILED:
+ case GROUP_UPDATE_FAILED:
+ case GROUP_REMOVE_FAILED:
+ post(event);
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ private class InternalGroupProviderService
+ extends AbstractProviderService<GroupProvider>
+ implements GroupProviderService {
+
+ protected InternalGroupProviderService(GroupProvider provider) {
+ super(provider);
+ }
+
+ @Override
+ public void groupOperationFailed(DeviceId deviceId, GroupOperation operation) {
+ store.groupOperationFailed(deviceId, operation);
+ }
+
+ @Override
+ public void pushGroupMetrics(DeviceId deviceId,
+ Collection<Group> groupEntries) {
+ log.trace("Received group metrics from device {}", deviceId);
+ checkValidity();
+ store.pushGroupMetrics(deviceId, groupEntries);
+ }
+ }
+
+ private class InternalDeviceListener implements DeviceListener {
+
+ @Override
+ public void event(DeviceEvent event) {
+ switch (event.type()) {
+ case DEVICE_REMOVED:
+ case DEVICE_AVAILABILITY_CHANGED:
+ if (!deviceService.isAvailable(event.subject().id())) {
+ log.debug("Device {} became un available; clearing initial audit status",
+ event.type(), event.subject().id());
+ store.deviceInitialAuditCompleted(event.subject().id(), false);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/group/impl/package-info.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/group/impl/package-info.java
new file mode 100644
index 00000000..641ab441
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/group/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Core subsystem for group state.
+ */
+package org.onosproject.net.group.impl; \ No newline at end of file
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/host/impl/BasicHostOperator.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/host/impl/BasicHostOperator.java
new file mode 100644
index 00000000..68aa27f0
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/host/impl/BasicHostOperator.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.host.impl;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+import org.slf4j.Logger;
+import org.onosproject.net.config.ConfigOperator;
+import org.onosproject.net.config.basics.BasicHostConfig;
+import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.SparseAnnotations;
+import org.onosproject.net.host.DefaultHostDescription;
+import org.onosproject.net.host.HostDescription;
+
+/**
+ * Implementations of merge policies for various sources of host configuration
+ * information. This includes applications, provides, and network configurations.
+ */
+public final class BasicHostOperator implements ConfigOperator {
+
+ protected static final double DEFAULT_COORD = -1.0;
+ private static final Logger log = getLogger(BasicHostOperator.class);
+
+ private BasicHostOperator() {
+ }
+
+ /**
+ * Generates a HostDescription containing fields from a HostDescription and
+ * a HostConfig.
+ *
+ * @param cfg the host config entity from network config
+ * @param descr a HostDescription
+ * @return HostDescription based on both sources
+ */
+ public static HostDescription combine(BasicHostConfig cfg, HostDescription descr) {
+ if (cfg == null) {
+ return descr;
+ }
+ SparseAnnotations sa = combine(cfg, descr.annotations());
+ return new DefaultHostDescription(descr.hwAddress(), descr.vlan(), descr.location(),
+ descr.ipAddress(), sa);
+ }
+
+ /**
+ * Generates an annotation from an existing annotation and HostConfig.
+ *
+ * @param cfg the device config entity from network config
+ * @param an the annotation
+ * @return annotation combining both sources
+ */
+ public static SparseAnnotations combine(BasicHostConfig cfg, SparseAnnotations an) {
+ DefaultAnnotations.Builder newBuilder = DefaultAnnotations.builder();
+ if (cfg.name() != null) {
+ newBuilder.set(AnnotationKeys.NAME, cfg.name());
+ }
+ if (cfg.latitude() != DEFAULT_COORD) {
+ newBuilder.set(AnnotationKeys.LATITUDE, Double.toString(cfg.latitude()));
+ }
+ if (cfg.longitude() != DEFAULT_COORD) {
+ newBuilder.set(AnnotationKeys.LONGITUDE, Double.toString(cfg.longitude()));
+ }
+ if (cfg.rackAddress() != null) {
+ newBuilder.set(AnnotationKeys.RACK_ADDRESS, cfg.rackAddress());
+ }
+ if (cfg.owner() != null) {
+ newBuilder.set(AnnotationKeys.OWNER, cfg.owner());
+ }
+ return DefaultAnnotations.union(an, newBuilder.build());
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/host/impl/HostManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/host/impl/HostManager.java
new file mode 100644
index 00000000..99263381
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/host/impl/HostManager.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.host.impl;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.incubator.net.intf.InterfaceService;
+import org.onosproject.net.provider.AbstractListenerProviderRegistry;
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.onosproject.net.config.NetworkConfigListener;
+import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.net.config.basics.BasicHostConfig;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.host.HostAdminService;
+import org.onosproject.net.host.HostDescription;
+import org.onosproject.net.host.HostEvent;
+import org.onosproject.net.host.HostListener;
+import org.onosproject.net.host.HostProvider;
+import org.onosproject.net.host.HostProviderRegistry;
+import org.onosproject.net.host.HostProviderService;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.host.HostStore;
+import org.onosproject.net.host.HostStoreDelegate;
+import org.onosproject.net.host.PortAddresses;
+import org.onosproject.net.packet.PacketService;
+import org.onosproject.net.provider.AbstractProviderService;
+import org.slf4j.Logger;
+
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static org.onosproject.security.AppGuard.checkPermission;
+import static org.slf4j.LoggerFactory.getLogger;
+import static org.onosproject.security.AppPermission.Type.*;
+
+/**
+ * Provides basic implementation of the host SB &amp; NB APIs.
+ */
+@Component(immediate = true)
+@Service
+public class HostManager
+ extends AbstractListenerProviderRegistry<HostEvent, HostListener, HostProvider, HostProviderService>
+ implements HostService, HostAdminService, HostProviderRegistry {
+
+ private final Logger log = getLogger(getClass());
+
+ public static final String HOST_ID_NULL = "Host ID cannot be null";
+
+ private final NetworkConfigListener networkConfigListener = new InternalNetworkConfigListener();
+
+ private HostStoreDelegate delegate = new InternalStoreDelegate();
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostStore store;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PacketService packetService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected NetworkConfigService networkConfigService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected InterfaceService interfaceService;
+
+ private HostMonitor monitor;
+
+ @Activate
+ public void activate() {
+ store.setDelegate(delegate);
+ eventDispatcher.addSink(HostEvent.class, listenerRegistry);
+ networkConfigService.addListener(networkConfigListener);
+ monitor = new HostMonitor(packetService, this, interfaceService);
+ monitor.start();
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ store.unsetDelegate(delegate);
+ eventDispatcher.removeSink(HostEvent.class);
+ networkConfigService.removeListener(networkConfigListener);
+ log.info("Stopped");
+ }
+
+ @Override
+ protected HostProviderService createProviderService(HostProvider provider) {
+ monitor.registerHostProvider(provider);
+ return new InternalHostProviderService(provider);
+ }
+
+ @Override
+ public int getHostCount() {
+ checkPermission(HOST_READ);
+ return store.getHostCount();
+ }
+
+ @Override
+ public Iterable<Host> getHosts() {
+ checkPermission(HOST_READ);
+ return store.getHosts();
+ }
+
+ @Override
+ public Host getHost(HostId hostId) {
+ checkPermission(HOST_READ);
+ checkNotNull(hostId, HOST_ID_NULL);
+ return store.getHost(hostId);
+ }
+
+ @Override
+ public Set<Host> getHostsByVlan(VlanId vlanId) {
+ checkPermission(HOST_READ);
+ return store.getHosts(vlanId);
+ }
+
+ @Override
+ public Set<Host> getHostsByMac(MacAddress mac) {
+ checkPermission(HOST_READ);
+ checkNotNull(mac, "MAC address cannot be null");
+ return store.getHosts(mac);
+ }
+
+ @Override
+ public Set<Host> getHostsByIp(IpAddress ip) {
+ checkPermission(HOST_READ);
+ checkNotNull(ip, "IP address cannot be null");
+ return store.getHosts(ip);
+ }
+
+ @Override
+ public Set<Host> getConnectedHosts(ConnectPoint connectPoint) {
+ checkPermission(HOST_READ);
+ checkNotNull(connectPoint, "Connection point cannot be null");
+ return store.getConnectedHosts(connectPoint);
+ }
+
+ @Override
+ public Set<Host> getConnectedHosts(DeviceId deviceId) {
+ checkPermission(HOST_READ);
+ checkNotNull(deviceId, "Device ID cannot be null");
+ return store.getConnectedHosts(deviceId);
+ }
+
+ @Override
+ public void startMonitoringIp(IpAddress ip) {
+ checkPermission(HOST_EVENT);
+ monitor.addMonitoringFor(ip);
+ }
+
+ @Override
+ public void stopMonitoringIp(IpAddress ip) {
+ checkPermission(HOST_EVENT);
+ monitor.stopMonitoring(ip);
+ }
+
+ @Override
+ public void requestMac(IpAddress ip) {
+ // FIXME!!!! Auto-generated method stub
+ }
+
+ @Override
+ public void removeHost(HostId hostId) {
+ checkNotNull(hostId, HOST_ID_NULL);
+ HostEvent event = store.removeHost(hostId);
+ if (event != null) {
+ post(event);
+ }
+ }
+
+ @Override
+ public void bindAddressesToPort(PortAddresses addresses) {
+ store.updateAddressBindings(addresses);
+ }
+
+ @Override
+ public void unbindAddressesFromPort(PortAddresses portAddresses) {
+ store.removeAddressBindings(portAddresses);
+ }
+
+ @Override
+ public void clearAddresses(ConnectPoint connectPoint) {
+ store.clearAddressBindings(connectPoint);
+ }
+
+ @Override
+ public Set<PortAddresses> getAddressBindings() {
+ checkPermission(HOST_READ);
+ return store.getAddressBindings();
+ }
+
+ @Override
+ public Set<PortAddresses> getAddressBindingsForPort(ConnectPoint connectPoint) {
+ checkPermission(HOST_READ);
+ return store.getAddressBindingsForPort(connectPoint);
+ }
+
+ // Personalized host provider service issued to the supplied provider.
+ private class InternalHostProviderService
+ extends AbstractProviderService<HostProvider>
+ implements HostProviderService {
+ InternalHostProviderService(HostProvider provider) {
+ super(provider);
+ }
+
+ @Override
+ public void hostDetected(HostId hostId, HostDescription hostDescription) {
+ checkNotNull(hostId, HOST_ID_NULL);
+ checkValidity();
+ hostDescription = validateHost(hostDescription, hostId);
+ HostEvent event = store.createOrUpdateHost(provider().id(), hostId,
+ hostDescription);
+ if (event != null) {
+ post(event);
+ }
+ }
+
+ // returns a HostDescription made from the union of the BasicHostConfig
+ // annotations if it exists
+ private HostDescription validateHost(HostDescription hostDescription, HostId hostId) {
+ BasicHostConfig cfg = networkConfigService.getConfig(hostId, BasicHostConfig.class);
+ checkState(cfg == null || cfg.isAllowed(), "Host {} is not allowed", hostId);
+
+ return BasicHostOperator.combine(cfg, hostDescription);
+ }
+
+ @Override
+ public void hostVanished(HostId hostId) {
+ checkNotNull(hostId, HOST_ID_NULL);
+ checkValidity();
+ HostEvent event = store.removeHost(hostId);
+ if (event != null) {
+ post(event);
+ }
+ }
+ }
+
+ // Store delegate to re-post events emitted from the store.
+ private class InternalStoreDelegate implements HostStoreDelegate {
+ @Override
+ public void notify(HostEvent event) {
+ post(event);
+ }
+ }
+
+ // listens for NetworkConfigEvents of type BasicHostConfig and removes
+ // links that the config does not allow
+ private class InternalNetworkConfigListener implements NetworkConfigListener {
+ @Override
+ public void event(NetworkConfigEvent event) {
+ if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
+ event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
+ event.configClass().equals(BasicHostConfig.class)) {
+ log.info("Detected Host network config event {}", event.type());
+ kickOutBadHost(((HostId) event.subject()));
+ }
+ }
+ }
+
+ // checks if the specified host is allowed by the BasicHostConfig
+ // and if not, removes it
+ private void kickOutBadHost(HostId hostId) {
+ BasicHostConfig cfg = networkConfigService.getConfig(hostId, BasicHostConfig.class);
+ if (cfg != null && !cfg.isAllowed()) {
+ Host badHost = getHost(hostId);
+ if (badHost != null) {
+ removeHost(hostId);
+ } else {
+ log.info("Failed removal: Host {} does not exist", hostId);
+ }
+ }
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/host/impl/HostMonitor.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/host/impl/HostMonitor.java
new file mode 100644
index 00000000..fe252368
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/host/impl/HostMonitor.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.host.impl;
+
+import org.jboss.netty.util.Timeout;
+import org.jboss.netty.util.TimerTask;
+import org.onlab.packet.ARP;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.ICMP6;
+import org.onlab.packet.IPv6;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onlab.packet.ndp.NeighborDiscoveryOptions;
+import org.onlab.packet.ndp.NeighborSolicitation;
+import org.onlab.util.Timer;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.incubator.net.intf.InterfaceService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Host;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.host.HostProvider;
+import org.onosproject.net.host.InterfaceIpAddress;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketService;
+import org.onosproject.net.provider.ProviderId;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Monitors hosts on the dataplane to detect changes in host data.
+ * <p>
+ * The HostMonitor can monitor hosts that have already been detected for
+ * changes. At an application's request, it can also monitor and actively
+ * probe for hosts that have not yet been detected (specified by IP address).
+ * </p>
+ */
+public class HostMonitor implements TimerTask {
+ private PacketService packetService;
+ private HostManager hostManager;
+ private InterfaceService interfaceService;
+
+ private final Set<IpAddress> monitoredAddresses;
+
+ private final ConcurrentMap<ProviderId, HostProvider> hostProviders;
+
+ private static final long DEFAULT_PROBE_RATE = 30000; // milliseconds
+ private static final byte[] ZERO_MAC_ADDRESS = MacAddress.ZERO.toBytes();
+ private long probeRate = DEFAULT_PROBE_RATE;
+
+ private Timeout timeout;
+
+ /**
+ * Creates a new host monitor.
+ *
+ * @param packetService packet service used to send packets on the data plane
+ * @param hostManager host manager used to look up host information and
+ * probe existing hosts
+ * @param interfaceService interface service for interface information
+ */
+ public HostMonitor(PacketService packetService, HostManager hostManager,
+ InterfaceService interfaceService) {
+
+ this.packetService = packetService;
+ this.hostManager = hostManager;
+ this.interfaceService = interfaceService;
+
+ monitoredAddresses = Collections.newSetFromMap(new ConcurrentHashMap<>());
+ hostProviders = new ConcurrentHashMap<>();
+ }
+
+ /**
+ * Adds an IP address to be monitored by the host monitor. The monitor will
+ * periodically probe the host to detect changes.
+ *
+ * @param ip IP address of the host to monitor
+ */
+ void addMonitoringFor(IpAddress ip) {
+ monitoredAddresses.add(ip);
+ }
+
+ /**
+ * Stops monitoring the given IP address.
+ *
+ * @param ip IP address to stop monitoring on
+ */
+ void stopMonitoring(IpAddress ip) {
+ monitoredAddresses.remove(ip);
+ }
+
+ /**
+ * Starts the host monitor. Does nothing if the monitor is already running.
+ */
+ void start() {
+ synchronized (this) {
+ if (timeout == null) {
+ timeout = Timer.getTimer().newTimeout(this, 0, TimeUnit.MILLISECONDS);
+ }
+ }
+ }
+
+ /**
+ * Stops the host monitor.
+ */
+ void shutdown() {
+ synchronized (this) {
+ timeout.cancel();
+ timeout = null;
+ }
+ }
+
+ /**
+ * Registers a host provider with the host monitor. The monitor can use the
+ * provider to probe hosts.
+ *
+ * @param provider the host provider to register
+ */
+ void registerHostProvider(HostProvider provider) {
+ hostProviders.put(provider.id(), provider);
+ }
+
+ @Override
+ public void run(Timeout timeout) throws Exception {
+ for (IpAddress ip : monitoredAddresses) {
+ Set<Host> hosts = hostManager.getHostsByIp(ip);
+
+ if (hosts.isEmpty()) {
+ sendArpNdpRequest(ip);
+ } else {
+ for (Host host : hosts) {
+ HostProvider provider = hostProviders.get(host.providerId());
+ if (provider == null) {
+ hostProviders.remove(host.providerId(), null);
+ } else {
+ provider.triggerProbe(host);
+ }
+ }
+ }
+ }
+
+ this.timeout = Timer.getTimer().newTimeout(this, probeRate, TimeUnit.MILLISECONDS);
+ }
+
+ /**
+ * Sends an ARP or Neighbor Discovery Protocol request for the given IP
+ * address.
+ *
+ * @param targetIp IP address to send the request for
+ */
+ private void sendArpNdpRequest(IpAddress targetIp) {
+ Interface intf = interfaceService.getMatchingInterface(targetIp);
+
+ if (intf == null) {
+ return;
+ }
+
+ for (InterfaceIpAddress ia : intf.ipAddresses()) {
+ if (ia.subnetAddress().contains(targetIp)) {
+ sendArpNdpProbe(intf.connectPoint(), targetIp, ia.ipAddress(),
+ intf.mac(), intf.vlan());
+ }
+ }
+ }
+
+ private void sendArpNdpProbe(ConnectPoint connectPoint,
+ IpAddress targetIp,
+ IpAddress sourceIp, MacAddress sourceMac,
+ VlanId vlan) {
+ Ethernet probePacket = null;
+
+ if (targetIp.isIp4()) {
+ // IPv4: Use ARP
+ probePacket = buildArpRequest(targetIp, sourceIp, sourceMac,
+ vlan);
+ } else {
+ // IPv6: Use Neighbor Discovery
+ probePacket = buildNdpRequest(targetIp, sourceIp, sourceMac,
+ vlan);
+ }
+
+ List<Instruction> instructions = new ArrayList<>();
+ instructions.add(Instructions.createOutput(connectPoint.port()));
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(connectPoint.port())
+ .build();
+
+ OutboundPacket outboundPacket =
+ new DefaultOutboundPacket(connectPoint.deviceId(), treatment,
+ ByteBuffer.wrap(probePacket.serialize()));
+
+ packetService.emit(outboundPacket);
+ }
+
+ private Ethernet buildArpRequest(IpAddress targetIp, IpAddress sourceIp,
+ MacAddress sourceMac, VlanId vlan) {
+
+ ARP arp = new ARP();
+ arp.setHardwareType(ARP.HW_TYPE_ETHERNET)
+ .setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH)
+ .setProtocolType(ARP.PROTO_TYPE_IP)
+ .setProtocolAddressLength((byte) IpAddress.INET_BYTE_LENGTH)
+ .setOpCode(ARP.OP_REQUEST);
+
+ arp.setSenderHardwareAddress(sourceMac.toBytes())
+ .setSenderProtocolAddress(sourceIp.toOctets())
+ .setTargetHardwareAddress(ZERO_MAC_ADDRESS)
+ .setTargetProtocolAddress(targetIp.toOctets());
+
+ Ethernet ethernet = new Ethernet();
+ ethernet.setEtherType(Ethernet.TYPE_ARP)
+ .setDestinationMACAddress(MacAddress.BROADCAST)
+ .setSourceMACAddress(sourceMac)
+ .setPayload(arp);
+
+ if (!vlan.equals(VlanId.NONE)) {
+ ethernet.setVlanID(vlan.toShort());
+ }
+
+ ethernet.setPad(true);
+
+ return ethernet;
+ }
+
+ private Ethernet buildNdpRequest(IpAddress targetIp, IpAddress sourceIp,
+ MacAddress sourceMac, VlanId vlan) {
+
+ // Create the Ethernet packet
+ Ethernet ethernet = new Ethernet();
+ ethernet.setEtherType(Ethernet.TYPE_IPV6)
+ .setDestinationMACAddress(MacAddress.BROADCAST)
+ .setSourceMACAddress(sourceMac);
+ if (!vlan.equals(VlanId.NONE)) {
+ ethernet.setVlanID(vlan.toShort());
+ }
+
+ //
+ // Create the IPv6 packet
+ //
+ // TODO: The destination IP address should be the
+ // solicited-node multicast address
+ IPv6 ipv6 = new IPv6();
+ ipv6.setSourceAddress(sourceIp.toOctets());
+ ipv6.setDestinationAddress(targetIp.toOctets());
+ ipv6.setHopLimit((byte) 255);
+
+ // Create the ICMPv6 packet
+ ICMP6 icmp6 = new ICMP6();
+ icmp6.setIcmpType(ICMP6.NEIGHBOR_SOLICITATION);
+ icmp6.setIcmpCode((byte) 0);
+
+ // Create the Neighbor Solication packet
+ NeighborSolicitation ns = new NeighborSolicitation();
+ ns.setTargetAddress(targetIp.toOctets());
+ ns.addOption(NeighborDiscoveryOptions.TYPE_SOURCE_LL_ADDRESS,
+ sourceMac.toBytes());
+
+ icmp6.setPayload(ns);
+ ipv6.setPayload(icmp6);
+ ethernet.setPayload(ipv6);
+
+ return ethernet;
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/host/impl/package-info.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/host/impl/package-info.java
new file mode 100644
index 00000000..1f7d5889
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/host/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Core subsystem for tracking global inventory of end-station hosts.
+ */
+package org.onosproject.net.host.impl;
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/CompilerRegistry.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/CompilerRegistry.java
new file mode 100644
index 00000000..1b70bc67
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/CompilerRegistry.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.intent.impl;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentCompiler;
+import org.onosproject.net.intent.IntentException;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+// TODO: consider a better name
+class CompilerRegistry {
+
+ private final ConcurrentMap<Class<? extends Intent>,
+ IntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>();
+
+ /**
+ * Registers the specified compiler for the given intent class.
+ *
+ * @param cls intent class
+ * @param compiler intent compiler
+ * @param <T> the type of intent
+ */
+ public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
+ compilers.put(cls, compiler);
+ }
+
+ /**
+ * Unregisters the compiler for the specified intent class.
+ *
+ * @param cls intent class
+ * @param <T> the type of intent
+ */
+ public <T extends Intent> void unregisterCompiler(Class<T> cls) {
+ compilers.remove(cls);
+ }
+
+ /**
+ * Returns immutable set of bindings of currently registered intent compilers.
+ *
+ * @return the set of compiler bindings
+ */
+ public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
+ return ImmutableMap.copyOf(compilers);
+ }
+
+ /**
+ * Compiles an intent recursively.
+ *
+ * @param intent intent
+ * @param previousInstallables previous intent installables
+ * @return result of compilation
+ */
+ List<Intent> compile(Intent intent, List<Intent> previousInstallables) {
+ if (intent.isInstallable()) {
+ return ImmutableList.of(intent);
+ }
+
+ registerSubclassCompilerIfNeeded(intent);
+ // FIXME: get previous resources
+ List<Intent> installable = new ArrayList<>();
+ for (Intent compiled : getCompiler(intent).compile(intent, previousInstallables, null)) {
+ installable.addAll(compile(compiled, previousInstallables));
+ }
+ return installable;
+ }
+
+ /**
+ * Returns the corresponding intent compiler to the specified intent.
+ *
+ * @param intent intent
+ * @param <T> the type of intent
+ * @return intent compiler corresponding to the specified intent
+ */
+ private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
+ @SuppressWarnings("unchecked")
+ IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
+ if (compiler == null) {
+ throw new IntentException("no compiler for class " + intent.getClass());
+ }
+ return compiler;
+ }
+
+ /**
+ * Registers an intent compiler of the specified intent if an intent compiler
+ * for the intent is not registered. This method traverses the class hierarchy of
+ * the intent. Once an intent compiler for a parent type is found, this method
+ * registers the found intent compiler.
+ *
+ * @param intent intent
+ */
+ private void registerSubclassCompilerIfNeeded(Intent intent) {
+ if (!compilers.containsKey(intent.getClass())) {
+ Class<?> cls = intent.getClass();
+ while (cls != Object.class) {
+ // As long as we're within the Intent class descendants
+ if (Intent.class.isAssignableFrom(cls)) {
+ IntentCompiler<?> compiler = compilers.get(cls);
+ if (compiler != null) {
+ compilers.put(intent.getClass(), compiler);
+ return;
+ }
+ }
+ cls = cls.getSuperclass();
+ }
+ }
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/IntentAccumulator.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/IntentAccumulator.java
new file mode 100644
index 00000000..54276ad4
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/IntentAccumulator.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.intent.impl;
+
+import com.google.common.collect.Maps;
+import org.onlab.util.AbstractAccumulator;
+import org.onosproject.net.intent.IntentBatchDelegate;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.Key;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Timer;
+
+/**
+ * An accumulator for building batches of intent operations. Only one batch should
+ * be in process per instance at a time.
+ */
+public class IntentAccumulator extends AbstractAccumulator<IntentData> {
+
+ private static final int DEFAULT_MAX_EVENTS = 1000;
+ private static final int DEFAULT_MAX_IDLE_MS = 10;
+ private static final int DEFAULT_MAX_BATCH_MS = 50;
+
+ // FIXME: Replace with a system-wide timer instance;
+ // TODO: Convert to use HashedWheelTimer or produce a variant of that; then decide which we want to adopt
+ private static final Timer TIMER = new Timer("onos-intent-op-batching");
+
+ private final IntentBatchDelegate delegate;
+
+ private volatile boolean ready;
+
+ /**
+ * Creates an intent operation accumulator.
+ *
+ * @param delegate the intent batch delegate
+ */
+ protected IntentAccumulator(IntentBatchDelegate delegate) {
+ super(TIMER, DEFAULT_MAX_EVENTS, DEFAULT_MAX_BATCH_MS, DEFAULT_MAX_IDLE_MS);
+ this.delegate = delegate;
+ // Assume that the delegate is ready for work at the start
+ ready = true; //TODO validate the assumption that delegate is ready
+ }
+
+ @Override
+ public void processItems(List<IntentData> items) {
+ ready = false;
+ delegate.execute(reduce(items));
+ }
+
+ private Collection<IntentData> reduce(List<IntentData> ops) {
+ Map<Key, IntentData> map = Maps.newHashMap();
+ for (IntentData op : ops) {
+ map.put(op.key(), op);
+ }
+ //TODO check the version... or maybe store will handle this.
+ return map.values();
+ }
+
+ @Override
+ public boolean isReady() {
+ return ready;
+ }
+
+ public void ready() {
+ ready = true;
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/IntentCleanup.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/IntentCleanup.java
new file mode 100644
index 00000000..d7fa3223
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/IntentCleanup.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.intent.impl;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Modified;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.IntentEvent;
+import org.onosproject.net.intent.IntentListener;
+import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.intent.IntentStore;
+import org.onosproject.net.intent.Key;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+
+import java.util.Dictionary;
+import java.util.Properties;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.ExecutorService;
+
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.get;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * This component cleans up intents that have encountered errors or otherwise
+ * stalled during installation or withdrawal.
+ * <p>
+ * It periodically polls (based on configured period) for pending and CORRUPT
+ * intents from the store and retries. It also listens for CORRUPT event
+ * notifications, which signify errors in processing, and retries.
+ * </p>
+ */
+@Component(immediate = true)
+public class IntentCleanup implements Runnable, IntentListener {
+
+ private static final Logger log = getLogger(IntentCleanup.class);
+
+ private static final int DEFAULT_PERIOD = 5; //seconds
+ private static final int DEFAULT_THRESHOLD = 5; //tries
+
+ @Property(name = "enabled", boolValue = true,
+ label = "Enables/disables the intent cleanup component")
+ private boolean enabled = true;
+
+ @Property(name = "period", intValue = DEFAULT_PERIOD,
+ label = "Frequency in ms between cleanup runs")
+ protected int period = DEFAULT_PERIOD;
+ private long periodMs;
+
+ @Property(name = "retryThreshold", intValue = DEFAULT_THRESHOLD,
+ label = "Number of times to retry CORRUPT intent without delay")
+ protected int retryThreshold = DEFAULT_THRESHOLD;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentService service;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentStore store;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ComponentConfigService cfgService;
+
+ private ExecutorService executor;
+ private Timer timer;
+ private TimerTask timerTask;
+
+ @Activate
+ public void activate() {
+ cfgService.registerProperties(getClass());
+ executor = newSingleThreadExecutor(groupedThreads("onos/intent", "cleanup"));
+ timer = new Timer("onos-intent-cleanup-timer");
+ service.addListener(this);
+ adjustRate();
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ cfgService.unregisterProperties(getClass(), false);
+ service.removeListener(this);
+ timer.cancel();
+ timerTask = null;
+ executor.shutdown();
+ log.info("Stopped");
+ }
+
+ @Modified
+ public void modified(ComponentContext context) {
+ Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
+
+ int newPeriod;
+ boolean newEnabled;
+ try {
+ String s = get(properties, "period");
+ newPeriod = isNullOrEmpty(s) ? period : Integer.parseInt(s.trim());
+
+ s = get(properties, "retryThreshold");
+ retryThreshold = isNullOrEmpty(s) ? retryThreshold : Integer.parseInt(s.trim());
+
+ s = get(properties, "enabled");
+ newEnabled = isNullOrEmpty(s) ? enabled : Boolean.parseBoolean(s.trim());
+ } catch (NumberFormatException e) {
+ log.warn(e.getMessage());
+ newPeriod = period;
+ newEnabled = enabled;
+ }
+
+ // Any change in the following parameters implies hard restart
+ if (newPeriod != period || enabled != newEnabled) {
+ period = newPeriod;
+ enabled = newEnabled;
+ adjustRate();
+ }
+
+ log.info("Settings: enabled={}, period={}, retryThreshold={}",
+ enabled, period, retryThreshold);
+ }
+
+ protected void adjustRate() {
+ if (timerTask != null) {
+ timerTask.cancel();
+ timerTask = null;
+ }
+
+ if (enabled) {
+ timerTask = new TimerTask() {
+ @Override
+ public void run() {
+ executor.submit(IntentCleanup.this);
+ }
+ };
+
+ periodMs = period * 1_000; //convert to ms
+ timer.scheduleAtFixedRate(timerTask, periodMs, periodMs);
+ }
+ }
+
+
+ @Override
+ public void run() {
+ try {
+ cleanup();
+ } catch (Exception e) {
+ log.warn("Caught exception during Intent cleanup", e);
+ }
+ }
+
+ private void resubmitCorrupt(IntentData intentData, boolean checkThreshold) {
+ if (checkThreshold && intentData.errorCount() >= retryThreshold) {
+ return; // threshold met or exceeded
+ }
+
+ switch (intentData.request()) {
+ case INSTALL_REQ:
+ service.submit(intentData.intent());
+ break;
+ case WITHDRAW_REQ:
+ service.withdraw(intentData.intent());
+ break;
+ default:
+ log.warn("Trying to resubmit corrupt/failed intent {} in state {} with request {}",
+ intentData.key(), intentData.state(), intentData.request());
+ break;
+ }
+ }
+
+ private void resubmitPendingRequest(IntentData intentData) {
+ switch (intentData.request()) {
+ case INSTALL_REQ:
+ service.submit(intentData.intent());
+ break;
+ case WITHDRAW_REQ:
+ service.withdraw(intentData.intent());
+ break;
+ default:
+ log.warn("Trying to resubmit pending intent {} in state {} with request {}",
+ intentData.key(), intentData.state(), intentData.request());
+ break;
+ }
+ }
+
+ /**
+ * Iterates through corrupt, failed and pending intents and
+ * re-submit/withdraw appropriately.
+ */
+ private void cleanup() {
+ int corruptCount = 0, failedCount = 0, stuckCount = 0, pendingCount = 0;
+
+ for (IntentData intentData : store.getIntentData(true, periodMs)) {
+ switch (intentData.state()) {
+ case FAILED:
+ resubmitCorrupt(intentData, false);
+ failedCount++;
+ break;
+ case CORRUPT:
+ resubmitCorrupt(intentData, false);
+ corruptCount++;
+ break;
+ case INSTALLING: //FALLTHROUGH
+ case WITHDRAWING:
+ resubmitPendingRequest(intentData);
+ stuckCount++;
+ break;
+ default:
+ //NOOP
+ break;
+ }
+ }
+
+ for (IntentData intentData : store.getPendingData(true, periodMs)) {
+ resubmitPendingRequest(intentData);
+ stuckCount++;
+ }
+
+ log.debug("Intent cleanup ran and resubmitted {} corrupt, {} failed, {} stuck, and {} pending intents",
+ corruptCount, failedCount, stuckCount, pendingCount);
+ }
+
+ @Override
+ public void event(IntentEvent event) {
+ // this is the fast path for CORRUPT intents, retry on event notification.
+ //TODO we might consider using the timer to back off for subsequent retries
+ if (enabled && event.type() == IntentEvent.Type.CORRUPT) {
+ Key key = event.subject().key();
+ if (store.isMaster(key)) {
+ IntentData data = store.getIntentData(event.subject().key());
+ resubmitCorrupt(data, true);
+ }
+ }
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/IntentCompilationException.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/IntentCompilationException.java
new file mode 100644
index 00000000..ae93336c
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/IntentCompilationException.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.intent.impl;
+
+import org.onosproject.net.intent.IntentException;
+
+/**
+ * An exception thrown when a intent compilation fails.
+ */
+public class IntentCompilationException extends IntentException {
+ private static final long serialVersionUID = 235237603018210810L;
+
+ public IntentCompilationException() {
+ super();
+ }
+
+ public IntentCompilationException(String message) {
+ super(message);
+ }
+
+ public IntentCompilationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/IntentInstallationException.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/IntentInstallationException.java
new file mode 100644
index 00000000..db21fe4a
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/IntentInstallationException.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.intent.impl;
+
+import org.onosproject.net.intent.IntentException;
+
+/**
+ * An exception thrown when intent installation fails.
+ */
+public class IntentInstallationException extends IntentException {
+ private static final long serialVersionUID = 3720268258616014168L;
+
+ public IntentInstallationException() {
+ super();
+ }
+
+ public IntentInstallationException(String message) {
+ super(message);
+ }
+
+ public IntentInstallationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/IntentManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/IntentManager.java
new file mode 100644
index 00000000..4c828e77
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/IntentManager.java
@@ -0,0 +1,488 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.intent.impl;
+
+import com.google.common.collect.ImmutableList;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.event.AbstractListenerManager;
+import org.onosproject.core.CoreService;
+import org.onosproject.core.IdGenerator;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleOperations;
+import org.onosproject.net.flow.FlowRuleOperationsContext;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.intent.FlowRuleIntent;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentBatchDelegate;
+import org.onosproject.net.intent.IntentCompiler;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.IntentEvent;
+import org.onosproject.net.intent.IntentExtensionService;
+import org.onosproject.net.intent.IntentListener;
+import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.intent.IntentState;
+import org.onosproject.net.intent.IntentStore;
+import org.onosproject.net.intent.IntentStoreDelegate;
+import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.impl.phase.FinalIntentProcessPhase;
+import org.onosproject.net.intent.impl.phase.IntentProcessPhase;
+import org.onosproject.net.intent.impl.phase.IntentWorker;
+import org.slf4j.Logger;
+
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.concurrent.Executors.newFixedThreadPool;
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.net.intent.IntentState.*;
+import static org.onosproject.net.intent.constraint.PartialFailureConstraint.intentAllowsPartialFailure;
+import static org.onosproject.net.intent.impl.phase.IntentProcessPhase.newInitialPhase;
+import static org.onosproject.security.AppGuard.checkPermission;
+import static org.slf4j.LoggerFactory.getLogger;
+import static org.onosproject.security.AppPermission.Type.*;
+
+
+/**
+ * An implementation of intent service.
+ */
+@Component(immediate = true)
+@Service
+public class IntentManager
+ extends AbstractListenerManager<IntentEvent, IntentListener>
+ implements IntentService, IntentExtensionService {
+
+ private static final Logger log = getLogger(IntentManager.class);
+
+ public static final String INTENT_NULL = "Intent cannot be null";
+ public static final String INTENT_ID_NULL = "Intent key cannot be null";
+
+ private static final int NUM_THREADS = 12;
+
+ private static final EnumSet<IntentState> RECOMPILE
+ = EnumSet.of(INSTALL_REQ, FAILED, WITHDRAW_REQ);
+ private static final EnumSet<IntentState> WITHDRAW
+ = EnumSet.of(WITHDRAW_REQ, WITHDRAWING, WITHDRAWN);
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentStore store;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ObjectiveTrackerService trackerService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowRuleService flowRuleService;
+
+ private ExecutorService batchExecutor;
+ private ExecutorService workerExecutor;
+
+ private final CompilerRegistry compilerRegistry = new CompilerRegistry();
+ private final InternalIntentProcessor processor = new InternalIntentProcessor();
+ private final IntentStoreDelegate delegate = new InternalStoreDelegate();
+ private final TopologyChangeDelegate topoDelegate = new InternalTopoChangeDelegate();
+ private final IntentBatchDelegate batchDelegate = new InternalBatchDelegate();
+ private IdGenerator idGenerator;
+
+ private final IntentAccumulator accumulator = new IntentAccumulator(batchDelegate);
+
+ @Activate
+ public void activate() {
+ store.setDelegate(delegate);
+ trackerService.setDelegate(topoDelegate);
+ eventDispatcher.addSink(IntentEvent.class, listenerRegistry);
+ batchExecutor = newSingleThreadExecutor(groupedThreads("onos/intent", "batch"));
+ workerExecutor = newFixedThreadPool(NUM_THREADS, groupedThreads("onos/intent", "worker-%d"));
+ idGenerator = coreService.getIdGenerator("intent-ids");
+ Intent.bindIdGenerator(idGenerator);
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ store.unsetDelegate(delegate);
+ trackerService.unsetDelegate(topoDelegate);
+ eventDispatcher.removeSink(IntentEvent.class);
+ batchExecutor.shutdown();
+ workerExecutor.shutdown();
+ Intent.unbindIdGenerator(idGenerator);
+ log.info("Stopped");
+ }
+
+ @Override
+ public void submit(Intent intent) {
+ checkPermission(INTENT_WRITE);
+ checkNotNull(intent, INTENT_NULL);
+ IntentData data = new IntentData(intent, IntentState.INSTALL_REQ, null);
+ store.addPending(data);
+ }
+
+ @Override
+ public void withdraw(Intent intent) {
+ checkPermission(INTENT_WRITE);
+ checkNotNull(intent, INTENT_NULL);
+ IntentData data = new IntentData(intent, IntentState.WITHDRAW_REQ, null);
+ store.addPending(data);
+ }
+
+ @Override
+ public void purge(Intent intent) {
+ checkPermission(INTENT_WRITE);
+ checkNotNull(intent, INTENT_NULL);
+ IntentData data = new IntentData(intent, IntentState.PURGE_REQ, null);
+ store.addPending(data);
+ }
+
+ @Override
+ public Intent getIntent(Key key) {
+ checkPermission(INTENT_READ);
+ return store.getIntent(key);
+ }
+
+ @Override
+ public Iterable<Intent> getIntents() {
+ checkPermission(INTENT_READ);
+ return store.getIntents();
+ }
+
+ @Override
+ public Iterable<IntentData> getIntentData() {
+ checkPermission(INTENT_READ);
+ return store.getIntentData(false, 0);
+ }
+
+ @Override
+ public long getIntentCount() {
+ checkPermission(INTENT_READ);
+ return store.getIntentCount();
+ }
+
+ @Override
+ public IntentState getIntentState(Key intentKey) {
+ checkPermission(INTENT_READ);
+ checkNotNull(intentKey, INTENT_ID_NULL);
+ return store.getIntentState(intentKey);
+ }
+
+ @Override
+ public List<Intent> getInstallableIntents(Key intentKey) {
+ checkPermission(INTENT_READ);
+ checkNotNull(intentKey, INTENT_ID_NULL);
+ return store.getInstallableIntents(intentKey);
+ }
+
+ @Override
+ public boolean isLocal(Key intentKey) {
+ checkPermission(INTENT_READ);
+ return store.isMaster(intentKey);
+ }
+
+ @Override
+ public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
+ compilerRegistry.registerCompiler(cls, compiler);
+ }
+
+ @Override
+ public <T extends Intent> void unregisterCompiler(Class<T> cls) {
+ compilerRegistry.unregisterCompiler(cls);
+ }
+
+ @Override
+ public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
+ return compilerRegistry.getCompilers();
+ }
+
+ @Override
+ public Iterable<Intent> getPending() {
+ checkPermission(INTENT_READ);
+
+ return store.getPending();
+ }
+
+ // Store delegate to re-post events emitted from the store.
+ private class InternalStoreDelegate implements IntentStoreDelegate {
+ @Override
+ public void notify(IntentEvent event) {
+ post(event);
+ }
+
+ @Override
+ public void process(IntentData data) {
+ accumulator.add(data);
+ }
+
+ @Override
+ public void onUpdate(IntentData intentData) {
+ trackerService.trackIntent(intentData);
+ }
+ }
+
+ private void buildAndSubmitBatches(Iterable<Key> intentKeys,
+ boolean compileAllFailed) {
+ // Attempt recompilation of the specified intents first.
+ for (Key key : intentKeys) {
+ Intent intent = store.getIntent(key);
+ if (intent == null) {
+ continue;
+ }
+ submit(intent);
+ }
+
+ // If required, compile all currently failed intents.
+ for (Intent intent : getIntents()) {
+ IntentState state = getIntentState(intent.key());
+ if ((compileAllFailed && RECOMPILE.contains(state))
+ || intentAllowsPartialFailure(intent)) {
+ if (WITHDRAW.contains(state)) {
+ withdraw(intent);
+ } else {
+ submit(intent);
+ }
+ }
+ }
+
+ //FIXME
+// for (ApplicationId appId : batches.keySet()) {
+// if (batchService.isLocalLeader(appId)) {
+// execute(batches.get(appId).build());
+// }
+// }
+ }
+
+ // Topology change delegate
+ private class InternalTopoChangeDelegate implements TopologyChangeDelegate {
+ @Override
+ public void triggerCompile(Iterable<Key> intentKeys,
+ boolean compileAllFailed) {
+ buildAndSubmitBatches(intentKeys, compileAllFailed);
+ }
+ }
+
+ private Future<FinalIntentProcessPhase> submitIntentData(IntentData data) {
+ IntentData current = store.getIntentData(data.key());
+ IntentProcessPhase initial = newInitialPhase(processor, data, current);
+ return workerExecutor.submit(new IntentWorker(initial));
+ }
+
+ private class IntentBatchProcess implements Runnable {
+
+ protected final Collection<IntentData> data;
+
+ IntentBatchProcess(Collection<IntentData> data) {
+ this.data = checkNotNull(data);
+ }
+
+ @Override
+ public void run() {
+ try {
+ /*
+ 1. wrap each intentdata in a runnable and submit
+ 2. wait for completion of all the work
+ 3. accumulate results and submit batch write of IntentData to store
+ (we can also try to update these individually)
+ */
+ submitUpdates(waitForFutures(createIntentUpdates()));
+ } catch (Exception e) {
+ log.error("Error submitting batches:", e);
+ // FIXME incomplete Intents should be cleaned up
+ // (transition to FAILED, etc.)
+
+ // the batch has failed
+ // TODO: maybe we should do more?
+ log.error("Walk the plank, matey...");
+ //FIXME
+// batchService.removeIntentOperations(data);
+ }
+ accumulator.ready();
+ }
+
+ private List<Future<FinalIntentProcessPhase>> createIntentUpdates() {
+ return data.stream()
+ .map(IntentManager.this::submitIntentData)
+ .collect(Collectors.toList());
+ }
+
+ private List<FinalIntentProcessPhase> waitForFutures(List<Future<FinalIntentProcessPhase>> futures) {
+ ImmutableList.Builder<FinalIntentProcessPhase> updateBuilder = ImmutableList.builder();
+ for (Future<FinalIntentProcessPhase> future : futures) {
+ try {
+ updateBuilder.add(future.get());
+ } catch (InterruptedException | ExecutionException e) {
+ //FIXME
+ log.warn("Future failed: {}", e);
+ }
+ }
+ return updateBuilder.build();
+ }
+
+ private void submitUpdates(List<FinalIntentProcessPhase> updates) {
+ store.batchWrite(updates.stream()
+ .map(FinalIntentProcessPhase::data)
+ .collect(Collectors.toList()));
+ }
+ }
+
+ private class InternalBatchDelegate implements IntentBatchDelegate {
+ @Override
+ public void execute(Collection<IntentData> operations) {
+ log.debug("Execute {} operation(s).", operations.size());
+ log.trace("Execute operations: {}", operations);
+
+ // batchExecutor is single-threaded, so only one batch is in flight at a time
+ batchExecutor.execute(new IntentBatchProcess(operations));
+ }
+ }
+
+ private class InternalIntentProcessor implements IntentProcessor {
+ @Override
+ public List<Intent> compile(Intent intent, List<Intent> previousInstallables) {
+ return compilerRegistry.compile(intent, previousInstallables);
+ }
+
+ @Override
+ public void apply(Optional<IntentData> toUninstall, Optional<IntentData> toInstall) {
+ IntentManager.this.apply(toUninstall, toInstall);
+ }
+ }
+
+ private enum Direction {
+ ADD,
+ REMOVE
+ }
+
+ private void applyIntentData(Optional<IntentData> intentData,
+ FlowRuleOperations.Builder builder,
+ Direction direction) {
+ if (!intentData.isPresent()) {
+ return;
+ }
+ IntentData data = intentData.get();
+
+ List<Intent> intentsToApply = data.installables();
+ if (!intentsToApply.stream().allMatch(x -> x instanceof FlowRuleIntent)) {
+ throw new IllegalStateException("installable intents must be FlowRuleIntent");
+ }
+
+ if (direction == Direction.ADD) {
+ trackerService.addTrackedResources(data.key(), data.intent().resources());
+ intentsToApply.forEach(installable ->
+ trackerService.addTrackedResources(data.key(), installable.resources()));
+ } else {
+ trackerService.removeTrackedResources(data.key(), data.intent().resources());
+ intentsToApply.forEach(installable ->
+ trackerService.removeTrackedResources(data.intent().key(),
+ installable.resources()));
+ }
+
+ // FIXME do FlowRuleIntents have stages??? Can we do uninstall work in parallel? I think so.
+ builder.newStage();
+
+ List<Collection<FlowRule>> stages = intentsToApply.stream()
+ .map(x -> (FlowRuleIntent) x)
+ .map(FlowRuleIntent::flowRules)
+ .collect(Collectors.toList());
+
+ for (Collection<FlowRule> rules : stages) {
+ if (direction == Direction.ADD) {
+ rules.forEach(builder::add);
+ } else {
+ rules.forEach(builder::remove);
+ }
+ }
+
+ }
+
+ private void apply(Optional<IntentData> toUninstall, Optional<IntentData> toInstall) {
+ // need to consider if FlowRuleIntent is only one as installable intent or not
+
+ FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
+ applyIntentData(toUninstall, builder, Direction.REMOVE);
+ applyIntentData(toInstall, builder, Direction.ADD);
+
+ FlowRuleOperations operations = builder.build(new FlowRuleOperationsContext() {
+ @Override
+ public void onSuccess(FlowRuleOperations ops) {
+ if (toInstall.isPresent()) {
+ IntentData installData = toInstall.get();
+ log.debug("Completed installing: {}", installData.key());
+ installData.setState(INSTALLED);
+ store.write(installData);
+ } else if (toUninstall.isPresent()) {
+ IntentData uninstallData = toUninstall.get();
+ log.debug("Completed withdrawing: {}", uninstallData.key());
+ switch (uninstallData.request()) {
+ case INSTALL_REQ:
+ uninstallData.setState(FAILED);
+ break;
+ case WITHDRAW_REQ:
+ default: //TODO "default" case should not happen
+ uninstallData.setState(WITHDRAWN);
+ break;
+ }
+ store.write(uninstallData);
+ }
+ }
+
+ @Override
+ public void onError(FlowRuleOperations ops) {
+ // if toInstall was cause of error, then recompile (manage/increment counter, when exceeded -> CORRUPT)
+ if (toInstall.isPresent()) {
+ IntentData installData = toInstall.get();
+ log.warn("Failed installation: {} {} on {}",
+ installData.key(), installData.intent(), ops);
+ installData.setState(CORRUPT);
+ installData.incrementErrorCount();
+ store.write(installData);
+ }
+ // if toUninstall was cause of error, then CORRUPT (another job will clean this up)
+ if (toUninstall.isPresent()) {
+ IntentData uninstallData = toUninstall.get();
+ log.warn("Failed withdrawal: {} {} on {}",
+ uninstallData.key(), uninstallData.intent(), ops);
+ uninstallData.setState(CORRUPT);
+ uninstallData.incrementErrorCount();
+ store.write(uninstallData);
+ }
+ }
+ });
+
+ if (log.isTraceEnabled()) {
+ log.trace("applying intent {} -> {} with {} rules: {}",
+ toUninstall.isPresent() ? toUninstall.get().key() : "<empty>",
+ toInstall.isPresent() ? toInstall.get().key() : "<empty>",
+ operations.stages().stream().mapToLong(i -> i.size()).sum(),
+ operations.stages());
+ }
+
+ flowRuleService.apply(operations);
+ }
+
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/IntentProcessor.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/IntentProcessor.java
new file mode 100644
index 00000000..5469c766
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/IntentProcessor.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.intent.impl;
+
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentData;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * A collection of methods to process an intent.
+ *
+ * This interface is public, but intended to be used only by IntentManager and
+ * IntentProcessPhase subclasses stored under phase package.
+ */
+public interface IntentProcessor {
+
+ /**
+ * Compiles an intent recursively.
+ *
+ * @param intent intent
+ * @param previousInstallables previous intent installables
+ * @return result of compilation
+ */
+ List<Intent> compile(Intent intent, List<Intent> previousInstallables);
+
+ /**
+ * @param toUninstall Intent data describing flows to uninstall.
+ * @param toInstall Intent data describing flows to install.
+ */
+ void apply(Optional<IntentData> toUninstall, Optional<IntentData> toInstall);
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/IntentRemovalException.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/IntentRemovalException.java
new file mode 100644
index 00000000..20530c0c
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/IntentRemovalException.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.intent.impl;
+
+import org.onosproject.net.intent.IntentException;
+
+/**
+ * An exception thrown when intent removal failed.
+ */
+public class IntentRemovalException extends IntentException {
+ private static final long serialVersionUID = -5259226322037891951L;
+
+ public IntentRemovalException() {
+ super();
+ }
+
+ public IntentRemovalException(String message) {
+ super(message);
+ }
+
+ public IntentRemovalException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/ObjectiveTracker.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/ObjectiveTracker.java
new file mode 100644
index 00000000..5710aced
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/ObjectiveTracker.java
@@ -0,0 +1,457 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.intent.impl;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.SetMultimap;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.ReferencePolicy;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.event.Event;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.ElementId;
+import org.onosproject.net.HostId;
+import org.onosproject.net.Link;
+import org.onosproject.net.LinkKey;
+import org.onosproject.net.NetworkResource;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.host.HostEvent;
+import org.onosproject.net.host.HostListener;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.PartitionEvent;
+import org.onosproject.net.intent.PartitionEventListener;
+import org.onosproject.net.intent.PartitionService;
+import org.onosproject.net.link.LinkEvent;
+import org.onosproject.net.resource.link.LinkResourceEvent;
+import org.onosproject.net.resource.link.LinkResourceListener;
+import org.onosproject.net.resource.link.LinkResourceService;
+import org.onosproject.net.topology.TopologyEvent;
+import org.onosproject.net.topology.TopologyListener;
+import org.onosproject.net.topology.TopologyService;
+import org.slf4j.Logger;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Multimaps.synchronizedSetMultimap;
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onlab.util.Tools.isNullOrEmpty;
+import static org.onosproject.net.LinkKey.linkKey;
+import static org.onosproject.net.intent.IntentState.INSTALLED;
+import static org.onosproject.net.intent.IntentState.INSTALLING;
+import static org.onosproject.net.link.LinkEvent.Type.LINK_REMOVED;
+import static org.onosproject.net.link.LinkEvent.Type.LINK_UPDATED;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Entity responsible for tracking installed flows and for monitoring topology
+ * events to determine what flows are affected by topology changes.
+ */
+@Component(immediate = true)
+@Service
+public class ObjectiveTracker implements ObjectiveTrackerService {
+
+ private final Logger log = getLogger(getClass());
+
+ private final ConcurrentMap<Key, Intent> intents = Maps.newConcurrentMap();
+
+ private final SetMultimap<LinkKey, Key> intentsByLink =
+ //TODO this could be slow as a point of synchronization
+ synchronizedSetMultimap(HashMultimap.<LinkKey, Key>create());
+
+ private final SetMultimap<ElementId, Key> intentsByDevice =
+ synchronizedSetMultimap(HashMultimap.<ElementId, Key>create());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected TopologyService topologyService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LinkResourceService resourceManager;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostService hostService;
+
+ @Reference(cardinality = ReferenceCardinality.OPTIONAL_UNARY,
+ policy = ReferencePolicy.DYNAMIC)
+ protected IntentService intentService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PartitionService partitionService;
+
+ private ExecutorService executorService =
+ newSingleThreadExecutor(groupedThreads("onos/intent", "objectivetracker"));
+ private ScheduledExecutorService executor = Executors
+ .newScheduledThreadPool(1);
+
+ private TopologyListener listener = new InternalTopologyListener();
+ private LinkResourceListener linkResourceListener =
+ new InternalLinkResourceListener();
+ private DeviceListener deviceListener = new InternalDeviceListener();
+ private HostListener hostListener = new InternalHostListener();
+ private PartitionEventListener partitionListener = new InternalPartitionListener();
+ private TopologyChangeDelegate delegate;
+
+ protected final AtomicBoolean updateScheduled = new AtomicBoolean(false);
+
+ @Activate
+ public void activate() {
+ topologyService.addListener(listener);
+ resourceManager.addListener(linkResourceListener);
+ deviceService.addListener(deviceListener);
+ hostService.addListener(hostListener);
+ partitionService.addListener(partitionListener);
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ topologyService.removeListener(listener);
+ resourceManager.removeListener(linkResourceListener);
+ deviceService.removeListener(deviceListener);
+ hostService.removeListener(hostListener);
+ partitionService.removeListener(partitionListener);
+ log.info("Stopped");
+ }
+
+ protected void bindIntentService(IntentService service) {
+ if (intentService == null) {
+ intentService = service;
+ }
+ }
+
+ protected void unbindIntentService(IntentService service) {
+ if (intentService == service) {
+ intentService = null;
+ }
+ }
+
+ @Override
+ public void setDelegate(TopologyChangeDelegate delegate) {
+ checkNotNull(delegate, "Delegate cannot be null");
+ checkArgument(this.delegate == null || this.delegate == delegate,
+ "Another delegate already set");
+ this.delegate = delegate;
+ }
+
+ @Override
+ public void unsetDelegate(TopologyChangeDelegate delegate) {
+ checkArgument(this.delegate == delegate, "Not the current delegate");
+ this.delegate = null;
+ }
+
+ @Override
+ public void addTrackedResources(Key intentKey,
+ Collection<NetworkResource> resources) {
+ for (NetworkResource resource : resources) {
+ if (resource instanceof Link) {
+ intentsByLink.put(linkKey((Link) resource), intentKey);
+ } else if (resource instanceof ElementId) {
+ intentsByDevice.put((ElementId) resource, intentKey);
+ }
+ }
+ }
+
+ @Override
+ public void removeTrackedResources(Key intentKey,
+ Collection<NetworkResource> resources) {
+ for (NetworkResource resource : resources) {
+ if (resource instanceof Link) {
+ intentsByLink.remove(linkKey((Link) resource), intentKey);
+ } else if (resource instanceof ElementId) {
+ intentsByDevice.remove(resource, intentKey);
+ }
+ }
+ }
+
+ @Override
+ public void trackIntent(IntentData intentData) {
+
+ //NOTE: This will be called for intents that are being added to the store
+ // locally (i.e. every intent update)
+
+ Key key = intentData.key();
+ Intent intent = intentData.intent();
+ boolean isLocal = intentService.isLocal(key);
+ boolean isInstalled = intentData.state() == INSTALLING ||
+ intentData.state() == INSTALLED;
+ List<Intent> installables = intentData.installables();
+
+ if (log.isTraceEnabled()) {
+ log.trace("intent {}, old: {}, new: {}, installableCount: {}, resourceCount: {}",
+ key,
+ intentsByDevice.values().contains(key),
+ isLocal && isInstalled,
+ installables.size(),
+ intent.resources().size() +
+ installables.stream()
+ .mapToLong(i -> i.resources().size()).sum());
+ }
+
+ if (isNullOrEmpty(installables) && intentData.state() == INSTALLED) {
+ log.warn("Intent {} is INSTALLED with no installables", key);
+ }
+
+ // FIXME Intents will be added 3 times (once directly using addTracked,
+ // then when installing and when installed)
+ if (isLocal && isInstalled) {
+ addTrackedResources(key, intent.resources());
+ for (Intent installable : installables) {
+ addTrackedResources(key, installable.resources());
+ }
+ // FIXME check all resources against current topo service(s); recompile if necessary
+ } else {
+ removeTrackedResources(key, intent.resources());
+ for (Intent installable : installables) {
+ removeTrackedResources(key, installable.resources());
+ }
+ }
+ }
+
+ // Internal re-actor to topology change events.
+ private class InternalTopologyListener implements TopologyListener {
+ @Override
+ public void event(TopologyEvent event) {
+ executorService.execute(new TopologyChangeHandler(event));
+ }
+ }
+
+ // Re-dispatcher of topology change events.
+ private class TopologyChangeHandler implements Runnable {
+
+ private final TopologyEvent event;
+
+ TopologyChangeHandler(TopologyEvent event) {
+ this.event = event;
+ }
+
+ @Override
+ public void run() {
+ // If there is no delegate, why bother? Just bail.
+ if (delegate == null) {
+ return;
+ }
+
+ if (event.reasons() == null || event.reasons().isEmpty()) {
+ delegate.triggerCompile(Collections.emptySet(), true);
+
+ } else {
+ Set<Key> intentsToRecompile = new HashSet<>();
+ boolean dontRecompileAllFailedIntents = true;
+
+ // Scan through the list of reasons and keep accruing all
+ // intents that need to be recompiled.
+ for (Event reason : event.reasons()) {
+ if (reason instanceof LinkEvent) {
+ LinkEvent linkEvent = (LinkEvent) reason;
+ final LinkKey linkKey = linkKey(linkEvent.subject());
+ synchronized (intentsByLink) {
+ Set<Key> intentKeys = intentsByLink.get(linkKey);
+ log.debug("recompile triggered by LinkEvent {} ({}) for {}",
+ linkKey, linkEvent.type(), intentKeys);
+ intentsToRecompile.addAll(intentKeys);
+ }
+ dontRecompileAllFailedIntents = dontRecompileAllFailedIntents &&
+ (linkEvent.type() == LINK_REMOVED ||
+ (linkEvent.type() == LINK_UPDATED &&
+ linkEvent.subject().isDurable()));
+ }
+ }
+ delegate.triggerCompile(intentsToRecompile, !dontRecompileAllFailedIntents);
+ }
+ }
+ }
+
+ /**
+ * Internal re-actor to resource available events.
+ */
+ private class InternalLinkResourceListener implements LinkResourceListener {
+ @Override
+ public void event(LinkResourceEvent event) {
+ executorService.execute(new ResourceAvailableHandler(event));
+ }
+ }
+
+ /*
+ * Re-dispatcher of resource available events.
+ */
+ private class ResourceAvailableHandler implements Runnable {
+
+ private final LinkResourceEvent event;
+
+ ResourceAvailableHandler(LinkResourceEvent event) {
+ this.event = event;
+ }
+
+ @Override
+ public void run() {
+ // If there is no delegate, why bother? Just bail.
+ if (delegate == null) {
+ return;
+ }
+
+ delegate.triggerCompile(Collections.emptySet(), true);
+ }
+ }
+
+ //TODO consider adding flow rule event tracking
+
+ private void updateTrackedResources(ApplicationId appId, boolean track) {
+ if (intentService == null) {
+ log.warn("Intent service is not bound yet");
+ return;
+ }
+ intentService.getIntents().forEach(intent -> {
+ if (intent.appId().equals(appId)) {
+ Key key = intent.key();
+ Collection<NetworkResource> resources = Lists.newArrayList();
+ intentService.getInstallableIntents(key).stream()
+ .map(installable -> installable.resources())
+ .forEach(resources::addAll);
+ if (track) {
+ addTrackedResources(key, resources);
+ } else {
+ removeTrackedResources(key, resources);
+ }
+ }
+ });
+ }
+
+ /*
+ * Re-dispatcher of device and host events.
+ */
+ private class DeviceAvailabilityHandler implements Runnable {
+
+ private final ElementId id;
+ private final boolean available;
+
+ DeviceAvailabilityHandler(ElementId id, boolean available) {
+ this.id = checkNotNull(id);
+ this.available = available;
+ }
+
+ @Override
+ public void run() {
+ // If there is no delegate, why bother? Just bail.
+ if (delegate == null) {
+ return;
+ }
+
+ // TODO should we recompile on available==true?
+ delegate.triggerCompile(intentsByDevice.get(id), available);
+ }
+ }
+
+
+ private class InternalDeviceListener implements DeviceListener {
+ @Override
+ public void event(DeviceEvent event) {
+ DeviceEvent.Type type = event.type();
+ switch (type) {
+ case DEVICE_ADDED:
+ case DEVICE_AVAILABILITY_CHANGED:
+ case DEVICE_REMOVED:
+ case DEVICE_SUSPENDED:
+ case DEVICE_UPDATED:
+ DeviceId id = event.subject().id();
+ // TODO we need to check whether AVAILABILITY_CHANGED means up or down
+ boolean available = (type == DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED ||
+ type == DeviceEvent.Type.DEVICE_ADDED ||
+ type == DeviceEvent.Type.DEVICE_UPDATED);
+ executorService.execute(new DeviceAvailabilityHandler(id, available));
+ break;
+ case PORT_ADDED:
+ case PORT_REMOVED:
+ case PORT_UPDATED:
+ case PORT_STATS_UPDATED:
+ default:
+ // Don't handle port events for now
+ break;
+ }
+ }
+ }
+
+ private class InternalHostListener implements HostListener {
+ @Override
+ public void event(HostEvent event) {
+ HostId id = event.subject().id();
+ HostEvent.Type type = event.type();
+ boolean available = (type == HostEvent.Type.HOST_ADDED);
+ executorService.execute(new DeviceAvailabilityHandler(id, available));
+ }
+ }
+
+ protected void doIntentUpdate() {
+ updateScheduled.set(false);
+ if (intentService == null) {
+ log.warn("Intent service is not bound yet");
+ return;
+ }
+ try {
+ //FIXME very inefficient
+ for (IntentData intentData : intentService.getIntentData()) {
+ try {
+ trackIntent(intentData);
+ } catch (NullPointerException npe) {
+ log.warn("intent error {}", intentData.key(), npe);
+ }
+ }
+ } catch (Exception e) {
+ log.warn("Exception caught during update task", e);
+ }
+ }
+
+ private void scheduleIntentUpdate(int afterDelaySec) {
+ if (updateScheduled.compareAndSet(false, true)) {
+ executor.schedule(this::doIntentUpdate, afterDelaySec, TimeUnit.SECONDS);
+ }
+ }
+
+ private final class InternalPartitionListener implements PartitionEventListener {
+ @Override
+ public void event(PartitionEvent event) {
+ log.debug("got message {}", event.subject());
+ scheduleIntentUpdate(1);
+ }
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/ObjectiveTrackerService.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/ObjectiveTrackerService.java
new file mode 100644
index 00000000..b7d367d7
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/ObjectiveTrackerService.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.intent.impl;
+
+import org.onosproject.net.NetworkResource;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.Key;
+
+import java.util.Collection;
+
+/**
+ * Auxiliary service for tracking intent path flows and for notifying the
+ * intent service of environment changes via topology change delegate.
+ */
+public interface ObjectiveTrackerService {
+
+ /**
+ * Sets a topology change delegate.
+ *
+ * @param delegate topology change delegate
+ */
+ void setDelegate(TopologyChangeDelegate delegate);
+
+ /**
+ * Unsets topology change delegate.
+ *
+ * @param delegate topology change delegate
+ */
+ void unsetDelegate(TopologyChangeDelegate delegate);
+
+ /**
+ * Adds a path flow to be tracked.
+ *
+ * @param intentKey intent identity on whose behalf the path is being tracked
+ * @param resources resources to track
+ */
+ // TODO consider using the IntentData here rather than just the key
+ void addTrackedResources(Key intentKey,
+ Collection<NetworkResource> resources);
+
+ /**
+ * Removes a path flow to be tracked.
+ *
+ * @param intentKey intent identity on whose behalf the path is being tracked
+ * @param resources resources to stop tracking
+ */
+ void removeTrackedResources(Key intentKey,
+ Collection<NetworkResource> resources);
+
+ /**
+ * Submits the specified intent data to be tracked.
+ *
+ * @param intentData intent data object to be tracked
+ */
+ void trackIntent(IntentData intentData);
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/PathNotFoundException.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/PathNotFoundException.java
new file mode 100644
index 00000000..c06e9fd2
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/PathNotFoundException.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.intent.impl;
+
+import com.google.common.base.MoreObjects;
+import org.onosproject.net.ElementId;
+import org.onosproject.net.intent.IntentException;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * An exception thrown when a path is not found.
+ */
+public class PathNotFoundException extends IntentException {
+ private static final long serialVersionUID = -2087045731049914733L;
+
+ private final ElementId source;
+ private final ElementId destination;
+
+ public PathNotFoundException(ElementId source, ElementId destination) {
+ super(String.format("No path from %s to %s", source, destination));
+ this.source = checkNotNull(source);
+ this.destination = checkNotNull(destination);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("source", source)
+ .add("destination", destination)
+ .toString();
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/TopologyChangeDelegate.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/TopologyChangeDelegate.java
new file mode 100644
index 00000000..49b114d5
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/TopologyChangeDelegate.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.intent.impl;
+
+import org.onosproject.net.intent.Key;
+
+/**
+ * Auxiliary delegate for integration of intent manager and flow trackerService.
+ */
+public interface TopologyChangeDelegate {
+
+ /**
+ * Notifies that topology has changed in such a way that the specified
+ * intents should be recompiled. If the {@code compileAllFailed} parameter
+ * is true, then all intents in {@link org.onosproject.net.intent.IntentState#FAILED}
+ * state should be compiled as well.
+ *
+ * @param intentIds intents that should be recompiled
+ * @param compileAllFailed true implies full compile of all failed intents
+ * is required; false for selective recompile only
+ */
+ void triggerCompile(Iterable<Key> intentIds, boolean compileAllFailed);
+
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/ConnectivityIntentCompiler.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/ConnectivityIntentCompiler.java
new file mode 100644
index 00000000..6de4cfdc
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/ConnectivityIntentCompiler.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.intent.impl.compiler;
+
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onosproject.net.ElementId;
+import org.onosproject.net.Path;
+import org.onosproject.net.intent.ConnectivityIntent;
+import org.onosproject.net.intent.Constraint;
+import org.onosproject.net.intent.IntentCompiler;
+import org.onosproject.net.intent.IntentExtensionService;
+import org.onosproject.net.intent.impl.PathNotFoundException;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.net.resource.link.LinkResourceService;
+import org.onosproject.net.topology.LinkWeight;
+import org.onosproject.net.topology.PathService;
+import org.onosproject.net.topology.TopologyEdge;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Base class for compilers of various
+ * {@link org.onosproject.net.intent.ConnectivityIntent connectivity intents}.
+ */
+@Component(immediate = true)
+public abstract class ConnectivityIntentCompiler<T extends ConnectivityIntent>
+ implements IntentCompiler<T> {
+
+ private static final ProviderId PID = new ProviderId("core", "org.onosproject.core", true);
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentExtensionService intentManager;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PathService pathService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LinkResourceService resourceService;
+
+ /**
+ * Returns an edge-weight capable of evaluating links on the basis of the
+ * specified constraints.
+ *
+ * @param constraints path constraints
+ * @return edge-weight function
+ */
+ protected LinkWeight weight(List<Constraint> constraints) {
+ return new ConstraintBasedLinkWeight(constraints);
+ }
+
+ /**
+ * Validates the specified path against the given constraints.
+ *
+ * @param path path to be checked
+ * @param constraints path constraints
+ * @return true if the path passes all constraints
+ */
+ protected boolean checkPath(Path path, List<Constraint> constraints) {
+ for (Constraint constraint : constraints) {
+ if (!constraint.validate(path, resourceService)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Computes a path between two ConnectPoints.
+ *
+ * @param intent intent on which behalf path is being computed
+ * @param one start of the path
+ * @param two end of the path
+ * @return Path between the two
+ * @throws PathNotFoundException if a path cannot be found
+ */
+ protected Path getPath(ConnectivityIntent intent,
+ ElementId one, ElementId two) {
+ Set<Path> paths = pathService.getPaths(one, two, weight(intent.constraints()));
+ final List<Constraint> constraints = intent.constraints();
+ ImmutableList<Path> filtered = FluentIterable.from(paths)
+ .filter(path -> checkPath(path, constraints))
+ .toList();
+ if (filtered.isEmpty()) {
+ throw new PathNotFoundException(one, two);
+ }
+ // TODO: let's be more intelligent about this eventually
+ return filtered.iterator().next();
+ }
+
+ /**
+ * Edge-weight capable of evaluating link cost using a set of constraints.
+ */
+ protected class ConstraintBasedLinkWeight implements LinkWeight {
+
+ private final List<Constraint> constraints;
+
+ /**
+ * Creates a new edge-weight function capable of evaluating links
+ * on the basis of the specified constraints.
+ *
+ * @param constraints path constraints
+ */
+ ConstraintBasedLinkWeight(List<Constraint> constraints) {
+ if (constraints == null) {
+ this.constraints = Collections.emptyList();
+ } else {
+ this.constraints = ImmutableList.copyOf(constraints);
+ }
+ }
+
+ @Override
+ public double weight(TopologyEdge edge) {
+ if (!constraints.iterator().hasNext()) {
+ return 1.0;
+ }
+
+ // iterate over all constraints in order and return the weight of
+ // the first one with fast fail over the first failure
+ Iterator<Constraint> it = constraints.iterator();
+
+ double cost = it.next().cost(edge.link(), resourceService);
+ while (it.hasNext() && cost > 0) {
+ if (it.next().cost(edge.link(), resourceService) < 0) {
+ return -1;
+ }
+ }
+ return cost;
+
+ }
+ }
+
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/HostToHostIntentCompiler.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/HostToHostIntentCompiler.java
new file mode 100644
index 00000000..41168258
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/HostToHostIntentCompiler.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.intent.impl.compiler;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onosproject.net.DefaultLink;
+import org.onosproject.net.DefaultPath;
+import org.onosproject.net.Host;
+import org.onosproject.net.Link;
+import org.onosproject.net.Path;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.intent.HostToHostIntent;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.PathIntent;
+import org.onosproject.net.intent.constraint.AsymmetricPathConstraint;
+import org.onosproject.net.resource.link.LinkResourceAllocations;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+import static org.onosproject.net.flow.DefaultTrafficSelector.builder;
+
+/**
+ * A intent compiler for {@link HostToHostIntent}.
+ */
+@Component(immediate = true)
+public class HostToHostIntentCompiler
+ extends ConnectivityIntentCompiler<HostToHostIntent> {
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostService hostService;
+
+ @Activate
+ public void activate() {
+ intentManager.registerCompiler(HostToHostIntent.class, this);
+ }
+
+ @Deactivate
+ public void deactivate() {
+ intentManager.unregisterCompiler(HostToHostIntent.class);
+ }
+
+ @Override
+ public List<Intent> compile(HostToHostIntent intent, List<Intent> installable,
+ Set<LinkResourceAllocations> resources) {
+ boolean isAsymmetric = intent.constraints().contains(new AsymmetricPathConstraint());
+ Path pathOne = getPath(intent, intent.one(), intent.two());
+ Path pathTwo = isAsymmetric ?
+ getPath(intent, intent.two(), intent.one()) : invertPath(pathOne);
+
+ Host one = hostService.getHost(intent.one());
+ Host two = hostService.getHost(intent.two());
+
+ return Arrays.asList(createPathIntent(pathOne, one, two, intent),
+ createPathIntent(pathTwo, two, one, intent));
+ }
+
+ // Inverts the specified path. This makes an assumption that each link in
+ // the path has a reverse link available. Under most circumstances, this
+ // assumption will hold.
+ private Path invertPath(Path path) {
+ List<Link> reverseLinks = new ArrayList<>(path.links().size());
+ for (Link link : path.links()) {
+ reverseLinks.add(0, reverseLink(link));
+ }
+ return new DefaultPath(path.providerId(), reverseLinks, path.cost());
+ }
+
+ // Produces a reverse variant of the specified link.
+ private Link reverseLink(Link link) {
+ return new DefaultLink(link.providerId(), link.dst(), link.src(),
+ link.type(), link.state(), link.isDurable());
+ }
+
+ // Creates a path intent from the specified path and original connectivity intent.
+ private Intent createPathIntent(Path path, Host src, Host dst,
+ HostToHostIntent intent) {
+ TrafficSelector selector = builder(intent.selector())
+ .matchEthSrc(src.mac()).matchEthDst(dst.mac()).build();
+ return PathIntent.builder()
+ .appId(intent.appId())
+ .selector(selector)
+ .treatment(intent.treatment())
+ .path(path)
+ .constraints(intent.constraints())
+ .priority(intent.priority())
+ .build();
+ }
+
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompiler.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompiler.java
new file mode 100644
index 00000000..76c5736d
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/LinkCollectionIntentCompiler.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.intent.impl.compiler;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.SetMultimap;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.intent.FlowRuleIntent;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentCompiler;
+import org.onosproject.net.intent.IntentExtensionService;
+import org.onosproject.net.intent.LinkCollectionIntent;
+import org.onosproject.net.resource.link.LinkResourceAllocations;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@Component(immediate = true)
+public class LinkCollectionIntentCompiler implements IntentCompiler<LinkCollectionIntent> {
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentExtensionService intentManager;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ private ApplicationId appId;
+
+ @Activate
+ public void activate() {
+ appId = coreService.registerApplication("org.onosproject.net.intent");
+ intentManager.registerCompiler(LinkCollectionIntent.class, this);
+ }
+
+ @Deactivate
+ public void deactivate() {
+ intentManager.unregisterCompiler(LinkCollectionIntent.class);
+ }
+
+ @Override
+ public List<Intent> compile(LinkCollectionIntent intent, List<Intent> installable,
+ Set<LinkResourceAllocations> resources) {
+ SetMultimap<DeviceId, PortNumber> inputPorts = HashMultimap.create();
+ SetMultimap<DeviceId, PortNumber> outputPorts = HashMultimap.create();
+
+ for (Link link : intent.links()) {
+ inputPorts.put(link.dst().deviceId(), link.dst().port());
+ outputPorts.put(link.src().deviceId(), link.src().port());
+ }
+
+ for (ConnectPoint ingressPoint : intent.ingressPoints()) {
+ inputPorts.put(ingressPoint.deviceId(), ingressPoint.port());
+ }
+
+ for (ConnectPoint egressPoint : intent.egressPoints()) {
+ outputPorts.put(egressPoint.deviceId(), egressPoint.port());
+ }
+
+ List<FlowRule> rules = new ArrayList<>();
+ for (DeviceId deviceId: outputPorts.keys()) {
+ rules.addAll(createRules(intent, deviceId, inputPorts.get(deviceId), outputPorts.get(deviceId)));
+ }
+ return Collections.singletonList(new FlowRuleIntent(appId, rules, intent.resources()));
+ }
+
+ private List<FlowRule> createRules(LinkCollectionIntent intent, DeviceId deviceId,
+ Set<PortNumber> inPorts, Set<PortNumber> outPorts) {
+ Set<PortNumber> ingressPorts = intent.ingressPoints().stream()
+ .filter(point -> point.deviceId().equals(deviceId))
+ .map(ConnectPoint::port)
+ .collect(Collectors.toSet());
+
+ TrafficTreatment.Builder defaultTreatmentBuilder = DefaultTrafficTreatment.builder();
+ outPorts.stream()
+ .forEach(defaultTreatmentBuilder::setOutput);
+ TrafficTreatment defaultTreatment = defaultTreatmentBuilder.build();
+
+ TrafficTreatment.Builder ingressTreatmentBuilder = DefaultTrafficTreatment.builder(intent.treatment());
+ outPorts.stream()
+ .forEach(ingressTreatmentBuilder::setOutput);
+ TrafficTreatment ingressTreatment = ingressTreatmentBuilder.build();
+
+ List<FlowRule> rules = new ArrayList<>(inPorts.size());
+ for (PortNumber inPort: inPorts) {
+ TrafficSelector selector = DefaultTrafficSelector.builder(intent.selector()).matchInPort(inPort).build();
+ TrafficTreatment treatment;
+ if (ingressPorts.contains(inPort)) {
+ treatment = ingressTreatment;
+ } else {
+ treatment = defaultTreatment;
+ }
+
+ FlowRule rule = DefaultFlowRule.builder()
+ .forDevice(deviceId)
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .withPriority(intent.priority())
+ .fromApp(appId)
+ .makePermanent()
+ .build();
+ rules.add(rule);
+ }
+
+ return rules;
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MplsIntentCompiler.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MplsIntentCompiler.java
new file mode 100644
index 00000000..609f9a34
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MplsIntentCompiler.java
@@ -0,0 +1,91 @@
+package org.onosproject.net.intent.impl.compiler;
+
+import static java.util.Arrays.asList;
+import static org.onosproject.net.DefaultEdgeLink.createEdgeLink;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultPath;
+import org.onosproject.net.Link;
+import org.onosproject.net.Path;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.MplsIntent;
+import org.onosproject.net.intent.MplsPathIntent;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.net.resource.link.LinkResourceAllocations;
+
+
+@Component(immediate = true)
+public class MplsIntentCompiler extends ConnectivityIntentCompiler<MplsIntent> {
+
+ // TODO: use off-the-shell core provider ID
+ private static final ProviderId PID =
+ new ProviderId("core", "org.onosproject.core", true);
+ // TODO: consider whether the default cost is appropriate or not
+ public static final int DEFAULT_COST = 1;
+
+
+ @Activate
+ public void activate() {
+ intentManager.registerCompiler(MplsIntent.class, this);
+ }
+
+ @Deactivate
+ public void deactivate() {
+ intentManager.unregisterCompiler(MplsIntent.class);
+ }
+
+ @Override
+ public List<Intent> compile(MplsIntent intent, List<Intent> installable,
+ Set<LinkResourceAllocations> resources) {
+ ConnectPoint ingressPoint = intent.ingressPoint();
+ ConnectPoint egressPoint = intent.egressPoint();
+
+ if (ingressPoint.deviceId().equals(egressPoint.deviceId())) {
+ List<Link> links = asList(createEdgeLink(ingressPoint, true), createEdgeLink(egressPoint, false));
+ return asList(createPathIntent(new DefaultPath(PID, links, DEFAULT_COST), intent));
+ }
+
+ List<Link> links = new ArrayList<>();
+ Path path = getPath(intent, ingressPoint.deviceId(),
+ egressPoint.deviceId());
+
+ links.add(createEdgeLink(ingressPoint, true));
+ links.addAll(path.links());
+
+ links.add(createEdgeLink(egressPoint, false));
+
+ return asList(createPathIntent(new DefaultPath(PID, links, path.cost(),
+ path.annotations()), intent));
+ }
+
+ /**
+ * Creates a path intent from the specified path and original
+ * connectivity intent.
+ *
+ * @param path path to create an intent for
+ * @param intent original intent
+ */
+ private Intent createPathIntent(Path path,
+ MplsIntent intent) {
+ return MplsPathIntent.builder()
+ .appId(intent.appId())
+ .selector(intent.selector())
+ .treatment(intent.treatment())
+ .path(path)
+ .ingressLabel(intent.ingressLabel())
+ .egressLabel(intent.egressLabel())
+ .constraints(intent.constraints())
+ .priority(intent.priority())
+ .build();
+ }
+
+
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MplsPathIntentCompiler.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MplsPathIntentCompiler.java
new file mode 100644
index 00000000..5fd1c85d
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MplsPathIntentCompiler.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.intent.impl.compiler;
+
+import com.google.common.collect.Sets;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.packet.EthType;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.VlanId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.EthTypeCriterion;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction;
+import org.onosproject.net.intent.FlowRuleIntent;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentCompiler;
+import org.onosproject.net.intent.IntentExtensionService;
+import org.onosproject.net.intent.MplsPathIntent;
+import org.onosproject.net.link.LinkStore;
+import org.onosproject.net.resource.link.DefaultLinkResourceRequest;
+import org.onosproject.net.resource.link.LinkResourceAllocations;
+import org.onosproject.net.resource.link.LinkResourceRequest;
+import org.onosproject.net.resource.link.LinkResourceService;
+import org.onosproject.net.resource.link.MplsLabel;
+import org.onosproject.net.resource.link.MplsLabelResourceAllocation;
+import org.onosproject.net.resource.ResourceAllocation;
+import org.onosproject.net.resource.ResourceType;
+import org.slf4j.Logger;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+@Component(immediate = true)
+public class MplsPathIntentCompiler implements IntentCompiler<MplsPathIntent> {
+
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentExtensionService intentExtensionService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LinkResourceService resourceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LinkStore linkStore;
+
+ protected ApplicationId appId;
+
+ @Override
+ public List<Intent> compile(MplsPathIntent intent, List<Intent> installable,
+ Set<LinkResourceAllocations> resources) {
+ LinkResourceAllocations allocations = assignMplsLabel(intent);
+ List<FlowRule> rules = generateRules(intent, allocations);
+
+ return Collections.singletonList(new FlowRuleIntent(appId, rules, intent.resources()));
+ }
+
+ @Activate
+ public void activate() {
+ appId = coreService.registerApplication("org.onosproject.net.intent");
+ intentExtensionService.registerCompiler(MplsPathIntent.class, this);
+ }
+
+ @Deactivate
+ public void deactivate() {
+ intentExtensionService.unregisterCompiler(MplsPathIntent.class);
+ }
+
+ private LinkResourceAllocations assignMplsLabel(MplsPathIntent intent) {
+ // TODO: do it better... Suggestions?
+ Set<Link> linkRequest = Sets.newHashSetWithExpectedSize(intent.path()
+ .links().size() - 2);
+ for (int i = 1; i <= intent.path().links().size() - 2; i++) {
+ Link link = intent.path().links().get(i);
+ linkRequest.add(link);
+ // add the inverse link. I want that the label is reserved both for
+ // the direct and inverse link
+ linkRequest.add(linkStore.getLink(link.dst(), link.src()));
+ }
+
+ LinkResourceRequest.Builder request = DefaultLinkResourceRequest
+ .builder(intent.id(), linkRequest).addMplsRequest();
+ LinkResourceAllocations reqMpls = resourceService
+ .requestResources(request.build());
+ return reqMpls;
+ }
+
+ private MplsLabel getMplsLabel(LinkResourceAllocations allocations, Link link) {
+ for (ResourceAllocation allocation : allocations
+ .getResourceAllocation(link)) {
+ if (allocation.type() == ResourceType.MPLS_LABEL) {
+ return ((MplsLabelResourceAllocation) allocation).mplsLabel();
+
+ }
+ }
+ log.warn("MPLS label was not assigned successfully");
+ return null;
+ }
+
+ private List<FlowRule> generateRules(MplsPathIntent intent,
+ LinkResourceAllocations allocations) {
+
+ Iterator<Link> links = intent.path().links().iterator();
+ Link srcLink = links.next();
+ ConnectPoint prev = srcLink.dst();
+
+ Link link = links.next();
+ // List of flow rules to be installed
+ List<FlowRule> rules = new LinkedList<>();
+
+ // Ingress traffic
+ // Get the new MPLS label
+ MplsLabel mpls = getMplsLabel(allocations, link);
+ checkNotNull(mpls);
+ MplsLabel prevLabel = mpls;
+ rules.add(ingressFlow(prev.port(), link, intent, mpls));
+
+ prev = link.dst();
+
+ while (links.hasNext()) {
+
+ link = links.next();
+
+ if (links.hasNext()) {
+ // Transit traffic
+ // Get the new MPLS label
+ mpls = getMplsLabel(allocations, link);
+ checkNotNull(mpls);
+ rules.add(transitFlow(prev.port(), link, intent,
+ prevLabel, mpls));
+ prevLabel = mpls;
+
+ } else {
+ // Egress traffic
+ rules.add(egressFlow(prev.port(), link, intent,
+ prevLabel));
+ }
+
+ prev = link.dst();
+ }
+ return rules;
+ }
+
+ private FlowRule ingressFlow(PortNumber inPort, Link link,
+ MplsPathIntent intent, MplsLabel label) {
+
+ TrafficSelector.Builder ingressSelector = DefaultTrafficSelector
+ .builder(intent.selector());
+ TrafficTreatment.Builder treat = DefaultTrafficTreatment.builder();
+ ingressSelector.matchInPort(inPort);
+
+ if (intent.ingressLabel().isPresent()) {
+ ingressSelector.matchEthType(Ethernet.MPLS_UNICAST)
+ .matchMplsLabel(intent.ingressLabel().get());
+
+ // Swap the MPLS label
+ treat.setMpls(label.label());
+ } else {
+ // Push and set the MPLS label
+ treat.pushMpls().setMpls(label.label());
+ }
+ // Add the output action
+ treat.setOutput(link.src().port());
+
+ return createFlowRule(intent, link.src().deviceId(), ingressSelector.build(), treat.build());
+ }
+
+ private FlowRule transitFlow(PortNumber inPort, Link link,
+ MplsPathIntent intent,
+ MplsLabel prevLabel,
+ MplsLabel outLabel) {
+
+ // Ignore the ingress Traffic Selector and use only the MPLS label
+ // assigned in the previous link
+ TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+ selector.matchInPort(inPort).matchEthType(Ethernet.MPLS_UNICAST)
+ .matchMplsLabel(prevLabel.label());
+ TrafficTreatment.Builder treat = DefaultTrafficTreatment.builder();
+
+ // Set the new label only if the label on the packet is
+ // different
+ if (!prevLabel.equals(outLabel)) {
+ treat.setMpls(outLabel.label());
+ }
+
+ treat.setOutput(link.src().port());
+ return createFlowRule(intent, link.src().deviceId(), selector.build(), treat.build());
+ }
+
+ private FlowRule egressFlow(PortNumber inPort, Link link,
+ MplsPathIntent intent,
+ MplsLabel prevLabel) {
+ // egress point: either set the egress MPLS label or pop the
+ // MPLS label based on the intent annotations
+
+ TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+ selector.matchInPort(inPort).matchEthType(Ethernet.MPLS_UNICAST)
+ .matchMplsLabel(prevLabel.label());
+
+ // apply the intent's treatments
+ TrafficTreatment.Builder treat = DefaultTrafficTreatment.builder(intent
+ .treatment());
+
+ // check if the treatement is popVlan or setVlan (rewrite),
+ // than selector needs to match any VlanId
+ for (Instruction instruct : intent.treatment().allInstructions()) {
+ if (instruct instanceof L2ModificationInstruction) {
+ L2ModificationInstruction l2Mod = (L2ModificationInstruction) instruct;
+ if (l2Mod.subtype() == L2ModificationInstruction.L2SubType.VLAN_PUSH) {
+ break;
+ }
+ if (l2Mod.subtype() == L2ModificationInstruction.L2SubType.VLAN_POP ||
+ l2Mod.subtype() == L2ModificationInstruction.L2SubType.VLAN_ID) {
+ selector.matchVlanId(VlanId.ANY);
+ }
+ }
+ }
+
+ if (intent.egressLabel().isPresent()) {
+ treat.setMpls(intent.egressLabel().get());
+ } else {
+ treat.popMpls(outputEthType(intent.selector()));
+ }
+ treat.setOutput(link.src().port());
+ return createFlowRule(intent, link.src().deviceId(),
+ selector.build(), treat.build());
+ }
+
+ protected FlowRule createFlowRule(MplsPathIntent intent, DeviceId deviceId,
+ TrafficSelector selector, TrafficTreatment treat) {
+ return DefaultFlowRule.builder()
+ .forDevice(deviceId)
+ .withSelector(selector)
+ .withTreatment(treat)
+ .withPriority(intent.priority())
+ .fromApp(appId)
+ .makePermanent()
+ .build();
+ }
+
+ // if the ingress ethertype is defined, the egress traffic
+ // will be use that value, otherwise the IPv4 ethertype is used.
+ private EthType outputEthType(TrafficSelector selector) {
+ Criterion c = selector.getCriterion(Criterion.Type.ETH_TYPE);
+ if (c != null && c instanceof EthTypeCriterion) {
+ EthTypeCriterion ethertype = (EthTypeCriterion) c;
+ return ethertype.ethType();
+ } else {
+ return EthType.EtherType.IPV4.ethType();
+ }
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MultiPointToSinglePointIntentCompiler.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MultiPointToSinglePointIntentCompiler.java
new file mode 100644
index 00000000..06d0e9a2
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/MultiPointToSinglePointIntentCompiler.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.intent.impl.compiler;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.Path;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentCompiler;
+import org.onosproject.net.intent.IntentException;
+import org.onosproject.net.intent.IntentExtensionService;
+import org.onosproject.net.intent.LinkCollectionIntent;
+import org.onosproject.net.intent.MultiPointToSinglePointIntent;
+import org.onosproject.net.intent.PointToPointIntent;
+import org.onosproject.net.resource.link.LinkResourceAllocations;
+import org.onosproject.net.topology.PathService;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.onosproject.net.intent.constraint.PartialFailureConstraint.intentAllowsPartialFailure;
+
+
+/**
+ * An intent compiler for
+ * {@link org.onosproject.net.intent.MultiPointToSinglePointIntent}.
+ */
+@Component(immediate = true)
+public class MultiPointToSinglePointIntentCompiler
+ implements IntentCompiler<MultiPointToSinglePointIntent> {
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentExtensionService intentManager;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PathService pathService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ @Activate
+ public void activate() {
+ intentManager.registerCompiler(MultiPointToSinglePointIntent.class, this);
+ }
+
+ @Deactivate
+ public void deactivate() {
+ intentManager.unregisterCompiler(PointToPointIntent.class);
+ }
+
+ @Override
+ public List<Intent> compile(MultiPointToSinglePointIntent intent, List<Intent> installable,
+ Set<LinkResourceAllocations> resources) {
+ Map<DeviceId, Link> links = new HashMap<>();
+ ConnectPoint egressPoint = intent.egressPoint();
+
+ final boolean allowMissingPaths = intentAllowsPartialFailure(intent);
+ boolean partialTree = false;
+ boolean anyMissingPaths = false;
+ for (ConnectPoint ingressPoint : intent.ingressPoints()) {
+ if (ingressPoint.deviceId().equals(egressPoint.deviceId())) {
+ if (deviceService.isAvailable(ingressPoint.deviceId())) {
+ partialTree = true;
+ } else {
+ anyMissingPaths = true;
+ }
+
+ continue;
+ }
+
+ Path path = getPath(ingressPoint, intent.egressPoint());
+ if (path != null) {
+ partialTree = true;
+
+ for (Link link : path.links()) {
+ if (links.containsKey(link.dst().deviceId())) {
+ // We've already reached the existing tree with the first
+ // part of this path. Add the merging point with different
+ // incoming port, but don't add the remainder of the path
+ // in case it differs from the path we already have.
+ links.put(link.src().deviceId(), link);
+ break;
+ }
+ links.put(link.src().deviceId(), link);
+ }
+ } else {
+ anyMissingPaths = true;
+ }
+ }
+
+ if (!partialTree) {
+ throw new IntentException("Could not find any paths between ingress and egress points.");
+ } else if (!allowMissingPaths && anyMissingPaths) {
+ throw new IntentException("Missing some paths between ingress and egress ports.");
+ }
+
+ Intent result = LinkCollectionIntent.builder()
+ .appId(intent.appId())
+ .selector(intent.selector())
+ .treatment(intent.treatment())
+ .links(Sets.newHashSet(links.values()))
+ .ingressPoints(intent.ingressPoints())
+ .egressPoints(ImmutableSet.of(intent.egressPoint()))
+ .priority(intent.priority())
+ .constraints(intent.constraints())
+ .build();
+
+ return Collections.singletonList(result);
+ }
+
+ /**
+ * Computes a path between two ConnectPoints.
+ *
+ * @param one start of the path
+ * @param two end of the path
+ * @return Path between the two
+ */
+ private Path getPath(ConnectPoint one, ConnectPoint two) {
+ Set<Path> paths = pathService.getPaths(one.deviceId(), two.deviceId());
+ if (paths.isEmpty()) {
+ return null;
+ }
+ // TODO: let's be more intelligent about this eventually
+ return paths.iterator().next();
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalCircuitIntentCompiler.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalCircuitIntentCompiler.java
new file mode 100644
index 00000000..99f58df7
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalCircuitIntentCompiler.java
@@ -0,0 +1,372 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.intent.impl.compiler;
+
+import com.google.common.collect.Sets;
+import org.apache.commons.lang3.tuple.Pair;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Modified;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.util.Tools;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.OchPort;
+import org.onosproject.net.OduCltPort;
+import org.onosproject.net.OduSignalType;
+import org.onosproject.net.Port;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.intent.FlowRuleIntent;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentCompiler;
+import org.onosproject.net.intent.IntentExtensionService;
+import org.onosproject.net.intent.IntentId;
+import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.intent.OpticalCircuitIntent;
+import org.onosproject.net.intent.OpticalConnectivityIntent;
+import org.onosproject.net.intent.impl.IntentCompilationException;
+import org.onosproject.net.resource.device.DeviceResourceService;
+import org.onosproject.net.resource.link.LinkResourceAllocations;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * An intent compiler for {@link org.onosproject.net.intent.OpticalCircuitIntent}.
+ */
+@Component(immediate = true)
+public class OpticalCircuitIntentCompiler implements IntentCompiler<OpticalCircuitIntent> {
+
+ private static final Logger log = LoggerFactory.getLogger(OpticalCircuitIntentCompiler.class);
+
+ private static final int DEFAULT_MAX_CAPACITY = 10;
+
+ @Property(name = "maxCapacity", intValue = DEFAULT_MAX_CAPACITY,
+ label = "Maximum utilization of an optical connection.")
+
+ private int maxCapacity = DEFAULT_MAX_CAPACITY;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ComponentConfigService cfgService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentExtensionService intentManager;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceResourceService deviceResourceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentService intentService;
+
+ private ApplicationId appId;
+
+ @Modified
+ public void modified(ComponentContext context) {
+ Dictionary properties = context.getProperties();
+
+ //TODO for reduction check if the new capacity is smaller than the size of the current mapping
+ String propertyString = Tools.get(properties, "maxCapacity");
+
+ //Ignore if propertyString is empty
+ if (!propertyString.isEmpty()) {
+ try {
+ int temp = Integer.parseInt(propertyString);
+ //Ensure value is non-negative but allow zero as a way to shutdown the link
+ if (temp >= 0) {
+ maxCapacity = temp;
+ }
+ } catch (NumberFormatException e) {
+ //Malformed arguments lead to no change of value (user should be notified of error)
+ log.error("The value '{}' for maxCapacity was not parsable as an integer.", propertyString, e);
+ }
+ } else {
+ //Notify of empty value but do not return (other properties will also go in this function)
+ log.error("The value for maxCapacity was set to an empty value.");
+ }
+
+ }
+
+ @Activate
+ public void activate(ComponentContext context) {
+ appId = coreService.registerApplication("org.onosproject.net.intent");
+ intentManager.registerCompiler(OpticalCircuitIntent.class, this);
+ cfgService.registerProperties(getClass());
+ modified(context);
+ }
+
+ @Deactivate
+ public void deactivate() {
+ intentManager.unregisterCompiler(OpticalCircuitIntent.class);
+ cfgService.unregisterProperties(getClass(), false);
+ }
+
+ @Override
+ public List<Intent> compile(OpticalCircuitIntent intent, List<Intent> installable,
+ Set<LinkResourceAllocations> resources) {
+ // Check if ports are OduClt ports
+ ConnectPoint src = intent.getSrc();
+ ConnectPoint dst = intent.getDst();
+ Port srcPort = deviceService.getPort(src.deviceId(), src.port());
+ Port dstPort = deviceService.getPort(dst.deviceId(), dst.port());
+ checkArgument(srcPort instanceof OduCltPort);
+ checkArgument(dstPort instanceof OduCltPort);
+
+ log.debug("Compiling optical circuit intent between {} and {}", src, dst);
+
+ // Reserve OduClt ports
+ if (!deviceResourceService.requestPorts(Sets.newHashSet(srcPort, dstPort), intent)) {
+ throw new IntentCompilationException("Unable to reserve ports for intent " + intent);
+ }
+
+ LinkedList<Intent> intents = new LinkedList<>();
+
+ FlowRuleIntent circuitIntent;
+ OpticalConnectivityIntent connIntent = findOpticalConnectivityIntent(intent);
+
+ // Create optical connectivity intent if needed
+ if (connIntent == null) {
+ // Find OCh ports with available resources
+ Pair<OchPort, OchPort> ochPorts = findPorts(intent);
+
+ if (ochPorts == null) {
+ return Collections.emptyList();
+ }
+
+ // Create optical connectivity intent
+ ConnectPoint srcCP = new ConnectPoint(src.elementId(), ochPorts.getLeft().number());
+ ConnectPoint dstCP = new ConnectPoint(dst.elementId(), ochPorts.getRight().number());
+ // FIXME: hardcoded ODU signal type
+ connIntent = OpticalConnectivityIntent.builder()
+ .appId(appId)
+ .src(srcCP)
+ .dst(dstCP)
+ .signalType(OduSignalType.ODU4)
+ .bidirectional(intent.isBidirectional())
+ .build();
+ intentService.submit(connIntent);
+ }
+
+ // Create optical circuit intent
+ List<FlowRule> rules = new LinkedList<>();
+ rules.add(connectPorts(src, connIntent.getSrc(), intent.priority()));
+ rules.add(connectPorts(connIntent.getDst(), dst, intent.priority()));
+
+ // Create flow rules for reverse path
+ if (intent.isBidirectional()) {
+ rules.add(connectPorts(connIntent.getSrc(), src, intent.priority()));
+ rules.add(connectPorts(dst, connIntent.getDst(), intent.priority()));
+ }
+
+ circuitIntent = new FlowRuleIntent(appId, rules, intent.resources());
+
+ // Save circuit to connectivity intent mapping
+ deviceResourceService.requestMapping(connIntent.id(), intent.id());
+ intents.add(circuitIntent);
+
+ return intents;
+ }
+
+ /**
+ * Checks if current allocations on given resource can satisfy request.
+ * If the resource is null, return true.
+ *
+ * @param request the intent making the request
+ * @param resource the resource on which to map the intent
+ * @return true if the resource can accept the request, false otherwise
+ */
+ private boolean isAvailable(Intent request, IntentId resource) {
+ if (resource == null) {
+ return true;
+ }
+
+ Set<IntentId> mapping = deviceResourceService.getMapping(resource);
+
+ if (mapping == null) {
+ return true;
+ }
+
+ return mapping.size() < maxCapacity;
+ }
+
+ private boolean isAllowed(OpticalCircuitIntent circuitIntent, OpticalConnectivityIntent connIntent) {
+ ConnectPoint srcStaticPort = staticPort(circuitIntent.getSrc());
+ if (srcStaticPort != null) {
+ if (!srcStaticPort.equals(connIntent.getSrc())) {
+ return false;
+ }
+ }
+
+ ConnectPoint dstStaticPort = staticPort(circuitIntent.getDst());
+ if (dstStaticPort != null) {
+ if (!dstStaticPort.equals(connIntent.getDst())) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns existing and available optical connectivity intent that matches the given circuit intent.
+ *
+ * @param circuitIntent optical circuit intent
+ * @return existing optical connectivity intent, null otherwise.
+ */
+ private OpticalConnectivityIntent findOpticalConnectivityIntent(OpticalCircuitIntent circuitIntent) {
+ for (Intent intent : intentService.getIntents()) {
+ if (!(intent instanceof OpticalConnectivityIntent)) {
+ continue;
+ }
+
+ OpticalConnectivityIntent connIntent = (OpticalConnectivityIntent) intent;
+
+ ConnectPoint src = circuitIntent.getSrc();
+ ConnectPoint dst = circuitIntent.getDst();
+ // Ignore if the intents don't have identical src and dst devices
+ if (!src.deviceId().equals(connIntent.getSrc().deviceId()) &&
+ !dst.deviceId().equals(connIntent.getDst().deviceId())) {
+ continue;
+ }
+
+ if (!isAllowed(circuitIntent, connIntent)) {
+ continue;
+ }
+
+ if (isAvailable(circuitIntent, connIntent.id())) {
+ return connIntent;
+ }
+ }
+
+ return null;
+ }
+
+ private ConnectPoint staticPort(ConnectPoint connectPoint) {
+ Port port = deviceService.getPort(connectPoint.deviceId(), connectPoint.port());
+
+ String staticPort = port.annotations().value(AnnotationKeys.STATIC_PORT);
+
+ // FIXME: need a better way to match the port
+ if (staticPort != null) {
+ for (Port p : deviceService.getPorts(connectPoint.deviceId())) {
+ if (staticPort.equals(p.number().name())) {
+ return new ConnectPoint(p.element().id(), p.number());
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private OchPort findAvailableOchPort(ConnectPoint oduPort, OpticalCircuitIntent circuitIntent) {
+ // First see if the port mappings are constrained
+ ConnectPoint ochCP = staticPort(oduPort);
+
+ if (ochCP != null) {
+ OchPort ochPort = (OchPort) deviceService.getPort(ochCP.deviceId(), ochCP.port());
+ IntentId intentId = deviceResourceService.getAllocations(ochPort);
+ if (isAvailable(circuitIntent, intentId)) {
+ return ochPort;
+ }
+ }
+
+ // No port constraints, so find any port that works
+ List<Port> ports = deviceService.getPorts(oduPort.deviceId());
+
+ for (Port port : ports) {
+ if (!(port instanceof OchPort)) {
+ continue;
+ }
+
+ IntentId intentId = deviceResourceService.getAllocations(port);
+ if (isAvailable(circuitIntent, intentId)) {
+ return (OchPort) port;
+ }
+ }
+
+ return null;
+ }
+
+ private Pair<OchPort, OchPort> findPorts(OpticalCircuitIntent intent) {
+
+ OchPort srcPort = findAvailableOchPort(intent.getSrc(), intent);
+ if (srcPort == null) {
+ return null;
+ }
+
+ OchPort dstPort = findAvailableOchPort(intent.getDst(), intent);
+ if (dstPort == null) {
+ return null;
+ }
+
+ return Pair.of(srcPort, dstPort);
+ }
+
+ /**
+ * Builds flow rule for mapping between two ports.
+ *
+ * @param src source port
+ * @param dst destination port
+ * @return flow rules
+ */
+ private FlowRule connectPorts(ConnectPoint src, ConnectPoint dst, int priority) {
+ checkArgument(src.deviceId().equals(dst.deviceId()));
+
+ TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
+ TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
+
+ selectorBuilder.matchInPort(src.port());
+ //selectorBuilder.add(Criteria.matchCltSignalType)
+ treatmentBuilder.setOutput(dst.port());
+ //treatmentBuilder.add(Instructions.modL1OduSignalType)
+
+ FlowRule flowRule = DefaultFlowRule.builder()
+ .forDevice(src.deviceId())
+ .withSelector(selectorBuilder.build())
+ .withTreatment(treatmentBuilder.build())
+ .withPriority(priority)
+ .fromApp(appId)
+ .makePermanent()
+ .build();
+
+ return flowRule;
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalConnectivityIntentCompiler.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalConnectivityIntentCompiler.java
new file mode 100644
index 00000000..c60325a7
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalConnectivityIntentCompiler.java
@@ -0,0 +1,305 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.intent.impl.compiler;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.util.Frequency;
+import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.OchPort;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.OchSignalType;
+import org.onosproject.net.OmsPort;
+import org.onosproject.net.Path;
+import org.onosproject.net.Port;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentCompiler;
+import org.onosproject.net.intent.IntentExtensionService;
+import org.onosproject.net.intent.OpticalConnectivityIntent;
+import org.onosproject.net.intent.OpticalPathIntent;
+import org.onosproject.net.intent.impl.IntentCompilationException;
+import org.onosproject.net.resource.ResourceAllocation;
+import org.onosproject.net.resource.ResourceType;
+import org.onosproject.net.resource.device.DeviceResourceService;
+import org.onosproject.net.resource.link.DefaultLinkResourceRequest;
+import org.onosproject.net.resource.link.LambdaResource;
+import org.onosproject.net.resource.link.LambdaResourceAllocation;
+import org.onosproject.net.resource.link.LinkResourceAllocations;
+import org.onosproject.net.resource.link.LinkResourceRequest;
+import org.onosproject.net.resource.link.LinkResourceService;
+import org.onosproject.net.topology.LinkWeight;
+import org.onosproject.net.topology.Topology;
+import org.onosproject.net.topology.TopologyEdge;
+import org.onosproject.net.topology.TopologyService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * An intent compiler for {@link org.onosproject.net.intent.OpticalConnectivityIntent}.
+ */
+@Component(immediate = true)
+public class OpticalConnectivityIntentCompiler implements IntentCompiler<OpticalConnectivityIntent> {
+
+ protected static final Logger log = LoggerFactory.getLogger(OpticalConnectivityIntentCompiler.class);
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentExtensionService intentManager;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected TopologyService topologyService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LinkResourceService linkResourceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceResourceService deviceResourceService;
+
+ @Activate
+ public void activate() {
+ intentManager.registerCompiler(OpticalConnectivityIntent.class, this);
+ }
+
+ @Deactivate
+ public void deactivate() {
+ intentManager.unregisterCompiler(OpticalConnectivityIntent.class);
+ }
+
+ @Override
+ public List<Intent> compile(OpticalConnectivityIntent intent,
+ List<Intent> installable,
+ Set<LinkResourceAllocations> resources) {
+ // Check if source and destination are optical OCh ports
+ ConnectPoint src = intent.getSrc();
+ ConnectPoint dst = intent.getDst();
+ Port srcPort = deviceService.getPort(src.deviceId(), src.port());
+ Port dstPort = deviceService.getPort(dst.deviceId(), dst.port());
+ checkArgument(srcPort instanceof OchPort);
+ checkArgument(dstPort instanceof OchPort);
+
+ log.debug("Compiling optical connectivity intent between {} and {}", src, dst);
+
+ // Reserve OCh ports
+ if (!deviceResourceService.requestPorts(ImmutableSet.of(srcPort, dstPort), intent)) {
+ throw new IntentCompilationException("Unable to reserve ports for intent " + intent);
+ }
+
+ // Calculate available light paths
+ Set<Path> paths = getOpticalPaths(intent);
+
+ // Use first path that can be successfully reserved
+ for (Path path : paths) {
+
+ // Static or dynamic lambda allocation
+ String staticLambda = srcPort.annotations().value(AnnotationKeys.STATIC_LAMBDA);
+ OchPort srcOchPort = (OchPort) srcPort;
+ OchPort dstOchPort = (OchPort) dstPort;
+ OchSignal ochSignal;
+
+ // FIXME: need to actually reserve the lambda for static lambda's
+ if (staticLambda != null) {
+ ochSignal = new OchSignal(Frequency.ofHz(Long.parseLong(staticLambda)),
+ srcOchPort.lambda().channelSpacing(),
+ srcOchPort.lambda().slotGranularity());
+ } else if (!srcOchPort.isTunable() || !dstOchPort.isTunable()) {
+ // FIXME: also check OCh port
+ ochSignal = srcOchPort.lambda();
+ } else {
+ // Request and reserve lambda on path
+ LinkResourceAllocations linkAllocs = assignWavelength(intent, path);
+ if (linkAllocs == null) {
+ continue;
+ }
+ LambdaResourceAllocation lambdaAlloc = getWavelength(path, linkAllocs);
+ OmsPort omsPort = (OmsPort) deviceService.getPort(path.src().deviceId(), path.src().port());
+ ochSignal = new OchSignal(lambdaAlloc.lambda().toInt(), omsPort.maxFrequency(), omsPort.grid());
+ }
+
+ // Create installable optical path intent
+ // Only support fixed grid for now
+ OchSignalType signalType = OchSignalType.FIXED_GRID;
+
+ Intent newIntent = OpticalPathIntent.builder()
+ .appId(intent.appId())
+ .src(intent.getSrc())
+ .dst(intent.getDst())
+ .path(path)
+ .lambda(ochSignal)
+ .signalType(signalType)
+ .bidirectional(intent.isBidirectional())
+ .build();
+
+ return ImmutableList.of(newIntent);
+ }
+
+ // Release port allocations if unsuccessful
+ deviceResourceService.releasePorts(intent.id());
+
+ throw new IntentCompilationException("Unable to find suitable lightpath for intent " + intent);
+ }
+
+ /**
+ * Find the lambda allocated to the path.
+ *
+ * @param path the path
+ * @param linkAllocs the link allocations
+ * @return lambda allocated to the given path
+ */
+ private LambdaResourceAllocation getWavelength(Path path, LinkResourceAllocations linkAllocs) {
+ for (Link link : path.links()) {
+ for (ResourceAllocation alloc : linkAllocs.getResourceAllocation(link)) {
+ if (alloc.type() == ResourceType.LAMBDA) {
+ return (LambdaResourceAllocation) alloc;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Request and reserve first available wavelength across path.
+ *
+ * @param path path in WDM topology
+ * @return first available lambda resource allocation
+ */
+ private LinkResourceAllocations assignWavelength(Intent intent, Path path) {
+ LinkResourceRequest.Builder request =
+ DefaultLinkResourceRequest.builder(intent.id(), path.links())
+ .addLambdaRequest();
+
+ LinkResourceAllocations allocations = linkResourceService.requestResources(request.build());
+
+ if (!checkWavelengthContinuity(allocations, path)) {
+ linkResourceService.releaseResources(allocations);
+ return null;
+ }
+
+ return allocations;
+ }
+
+ /**
+ * Checks wavelength continuity constraint across path, i.e., an identical lambda is used on all links.
+ * @return true if wavelength continuity is met, false otherwise
+ */
+ private boolean checkWavelengthContinuity(LinkResourceAllocations allocations, Path path) {
+ if (allocations == null) {
+ return false;
+ }
+
+ LambdaResource lambda = null;
+
+ for (Link link : path.links()) {
+ for (ResourceAllocation alloc : allocations.getResourceAllocation(link)) {
+ if (alloc.type() == ResourceType.LAMBDA) {
+ LambdaResource nextLambda = ((LambdaResourceAllocation) alloc).lambda();
+ if (nextLambda == null) {
+ return false;
+ }
+ if (lambda == null) {
+ lambda = nextLambda;
+ continue;
+ }
+ if (!lambda.equals(nextLambda)) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ private ConnectPoint staticPort(ConnectPoint connectPoint) {
+ Port port = deviceService.getPort(connectPoint.deviceId(), connectPoint.port());
+
+ String staticPort = port.annotations().value(AnnotationKeys.STATIC_PORT);
+
+ // FIXME: need a better way to match the port
+ if (staticPort != null) {
+ for (Port p : deviceService.getPorts(connectPoint.deviceId())) {
+ if (staticPort.equals(p.number().name())) {
+ return new ConnectPoint(p.element().id(), p.number());
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Calculates optical paths in WDM topology.
+ *
+ * @param intent optical connectivity intent
+ * @return set of paths in WDM topology
+ */
+ private Set<Path> getOpticalPaths(OpticalConnectivityIntent intent) {
+ // Route in WDM topology
+ Topology topology = topologyService.currentTopology();
+ LinkWeight weight = new LinkWeight() {
+ @Override
+ public double weight(TopologyEdge edge) {
+ // Disregard inactive or non-optical links
+ if (edge.link().state() == Link.State.INACTIVE) {
+ return -1;
+ }
+ if (edge.link().type() != Link.Type.OPTICAL) {
+ return -1;
+ }
+ // Adhere to static port mappings
+ DeviceId srcDeviceId = edge.link().src().deviceId();
+ if (srcDeviceId.equals(intent.getSrc().deviceId())) {
+ ConnectPoint srcStaticPort = staticPort(intent.getSrc());
+ if (srcStaticPort != null) {
+ return srcStaticPort.equals(edge.link().src()) ? 1 : -1;
+ }
+ }
+ DeviceId dstDeviceId = edge.link().dst().deviceId();
+ if (dstDeviceId.equals(intent.getDst().deviceId())) {
+ ConnectPoint dstStaticPort = staticPort(intent.getDst());
+ if (dstStaticPort != null) {
+ return dstStaticPort.equals(edge.link().dst()) ? 1 : -1;
+ }
+ }
+
+ return 1;
+ }
+ };
+
+ ConnectPoint start = intent.getSrc();
+ ConnectPoint end = intent.getDst();
+ Set<Path> paths = topologyService.getPaths(topology, start.deviceId(),
+ end.deviceId(), weight);
+
+ return paths;
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalPathIntentCompiler.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalPathIntentCompiler.java
new file mode 100644
index 00000000..ca9ae5cc
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/OpticalPathIntentCompiler.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.intent.impl.compiler;
+
+import com.google.common.collect.Lists;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Link;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.criteria.Criteria;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.intent.FlowRuleIntent;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentCompiler;
+import org.onosproject.net.intent.IntentExtensionService;
+import org.onosproject.net.intent.OpticalPathIntent;
+import org.onosproject.net.resource.link.LinkResourceAllocations;
+import org.onosproject.net.resource.link.LinkResourceService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+@Component(immediate = true)
+public class OpticalPathIntentCompiler implements IntentCompiler<OpticalPathIntent> {
+
+ private static final Logger log = LoggerFactory.getLogger(OpticalPathIntentCompiler.class);
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentExtensionService intentManager;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LinkResourceService resourceService;
+
+ private ApplicationId appId;
+
+ @Activate
+ public void activate() {
+ appId = coreService.registerApplication("org.onosproject.net.intent");
+ intentManager.registerCompiler(OpticalPathIntent.class, this);
+ }
+
+ @Deactivate
+ public void deactivate() {
+ intentManager.unregisterCompiler(OpticalPathIntent.class);
+ }
+
+ @Override
+ public List<Intent> compile(OpticalPathIntent intent, List<Intent> installable,
+ Set<LinkResourceAllocations> resources) {
+ log.debug("Compiling optical path intent between {} and {}", intent.src(), intent.dst());
+
+ // Create rules for forward and reverse path
+ List<FlowRule> rules = createRules(intent);
+ if (intent.isBidirectional()) {
+ rules.addAll(createReverseRules(intent));
+ }
+
+ return Collections.singletonList(new FlowRuleIntent(appId, rules, intent.resources()));
+ }
+
+ /**
+ * Create rules for the forward path of the intent.
+ *
+ * @param intent the intent
+ * @return list of flow rules
+ */
+ private List<FlowRule> createRules(OpticalPathIntent intent) {
+ TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
+ selectorBuilder.matchInPort(intent.src().port());
+
+ List<FlowRule> rules = new LinkedList<>();
+ ConnectPoint current = intent.src();
+
+ for (Link link : intent.path().links()) {
+ TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
+ treatmentBuilder.add(Instructions.modL0Lambda(intent.lambda()));
+ treatmentBuilder.setOutput(link.src().port());
+
+ FlowRule rule = DefaultFlowRule.builder()
+ .forDevice(current.deviceId())
+ .withSelector(selectorBuilder.build())
+ .withTreatment(treatmentBuilder.build())
+ .withPriority(intent.priority())
+ .fromApp(appId)
+ .makePermanent()
+ .build();
+
+ rules.add(rule);
+
+ current = link.dst();
+ selectorBuilder.matchInPort(link.dst().port());
+ selectorBuilder.add(Criteria.matchLambda(intent.lambda()));
+ selectorBuilder.add(Criteria.matchOchSignalType(intent.signalType()));
+ }
+
+ // Build the egress ROADM rule
+ TrafficTreatment.Builder treatmentLast = DefaultTrafficTreatment.builder();
+ treatmentLast.setOutput(intent.dst().port());
+
+ FlowRule rule = new DefaultFlowRule.Builder()
+ .forDevice(intent.dst().deviceId())
+ .withSelector(selectorBuilder.build())
+ .withTreatment(treatmentLast.build())
+ .withPriority(intent.priority())
+ .fromApp(appId)
+ .makePermanent()
+ .build();
+ rules.add(rule);
+
+ return rules;
+ }
+
+ /**
+ * Create rules for the reverse path of the intent.
+ *
+ * @param intent the intent
+ * @return list of flow rules
+ */
+ private List<FlowRule> createReverseRules(OpticalPathIntent intent) {
+ TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
+ selectorBuilder.matchInPort(intent.dst().port());
+
+ List<FlowRule> rules = new LinkedList<>();
+ ConnectPoint current = intent.dst();
+
+ for (Link link : Lists.reverse(intent.path().links())) {
+ TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
+ treatmentBuilder.add(Instructions.modL0Lambda(intent.lambda()));
+ treatmentBuilder.setOutput(link.dst().port());
+
+ FlowRule rule = DefaultFlowRule.builder()
+ .forDevice(current.deviceId())
+ .withSelector(selectorBuilder.build())
+ .withTreatment(treatmentBuilder.build())
+ .withPriority(intent.priority())
+ .fromApp(appId)
+ .makePermanent()
+ .build();
+
+ rules.add(rule);
+
+ current = link.src();
+ selectorBuilder.matchInPort(link.src().port());
+ selectorBuilder.add(Criteria.matchLambda(intent.lambda()));
+ selectorBuilder.add(Criteria.matchOchSignalType(intent.signalType()));
+ }
+
+ // Build the egress ROADM rule
+ TrafficTreatment.Builder treatmentLast = DefaultTrafficTreatment.builder();
+ treatmentLast.setOutput(intent.src().port());
+
+ FlowRule rule = new DefaultFlowRule.Builder()
+ .forDevice(intent.src().deviceId())
+ .withSelector(selectorBuilder.build())
+ .withTreatment(treatmentLast.build())
+ .withPriority(intent.priority())
+ .fromApp(appId)
+ .makePermanent()
+ .build();
+ rules.add(rule);
+
+ return rules;
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathIntentCompiler.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathIntentCompiler.java
new file mode 100644
index 00000000..7add2173
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PathIntentCompiler.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.intent.impl.compiler;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Link;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.intent.FlowRuleIntent;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentCompiler;
+import org.onosproject.net.intent.IntentExtensionService;
+import org.onosproject.net.intent.PathIntent;
+import org.onosproject.net.resource.link.LinkResourceAllocations;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+@Component(immediate = true)
+public class PathIntentCompiler implements IntentCompiler<PathIntent> {
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentExtensionService intentManager;
+
+ private ApplicationId appId;
+
+ @Activate
+ public void activate() {
+ appId = coreService.registerApplication("org.onosproject.net.intent");
+ intentManager.registerCompiler(PathIntent.class, this);
+ }
+
+ @Deactivate
+ public void deactivate() {
+ intentManager.unregisterCompiler(PathIntent.class);
+ }
+
+ @Override
+ public List<Intent> compile(PathIntent intent, List<Intent> installable,
+ Set<LinkResourceAllocations> resources) {
+ // Note: right now recompile is not considered
+ // TODO: implement recompile behavior
+
+ List<Link> links = intent.path().links();
+ List<FlowRule> rules = new ArrayList<>(links.size() - 1);
+
+ for (int i = 0; i < links.size() - 1; i++) {
+ ConnectPoint ingress = links.get(i).dst();
+ ConnectPoint egress = links.get(i + 1).src();
+ FlowRule rule = createFlowRule(intent.selector(), intent.treatment(),
+ ingress, egress, intent.priority(),
+ isLast(links, i));
+ rules.add(rule);
+ }
+
+ return Collections.singletonList(new FlowRuleIntent(appId, null, rules, intent.resources()));
+ }
+
+ private FlowRule createFlowRule(TrafficSelector originalSelector, TrafficTreatment originalTreatment,
+ ConnectPoint ingress, ConnectPoint egress,
+ int priority, boolean last) {
+ TrafficSelector selector = DefaultTrafficSelector.builder(originalSelector)
+ .matchInPort(ingress.port())
+ .build();
+
+ TrafficTreatment.Builder treatmentBuilder;
+ if (last) {
+ treatmentBuilder = DefaultTrafficTreatment.builder(originalTreatment);
+ } else {
+ treatmentBuilder = DefaultTrafficTreatment.builder();
+ }
+ TrafficTreatment treatment = treatmentBuilder.setOutput(egress.port()).build();
+
+ return DefaultFlowRule.builder()
+ .forDevice(ingress.deviceId())
+ .withSelector(selector)
+ .withTreatment(treatment)
+ .withPriority(priority)
+ .fromApp(appId)
+ .makePermanent()
+ .build();
+ }
+
+ private boolean isLast(List<Link> links, int i) {
+ return i == links.size() - 2;
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PointToPointIntentCompiler.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PointToPointIntentCompiler.java
new file mode 100644
index 00000000..5644ee22
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/PointToPointIntentCompiler.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.intent.impl.compiler;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultPath;
+import org.onosproject.net.Link;
+import org.onosproject.net.Path;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.PathIntent;
+import org.onosproject.net.intent.PointToPointIntent;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.net.resource.link.LinkResourceAllocations;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import static java.util.Arrays.asList;
+import static org.onosproject.net.DefaultEdgeLink.createEdgeLink;
+
+/**
+ * An intent compiler for {@link org.onosproject.net.intent.PointToPointIntent}.
+ */
+@Component(immediate = true)
+public class PointToPointIntentCompiler
+ extends ConnectivityIntentCompiler<PointToPointIntent> {
+
+ // TODO: use off-the-shell core provider ID
+ private static final ProviderId PID =
+ new ProviderId("core", "org.onosproject.core", true);
+ // TODO: consider whether the default cost is appropriate or not
+ public static final int DEFAULT_COST = 1;
+
+ @Activate
+ public void activate() {
+ intentManager.registerCompiler(PointToPointIntent.class, this);
+ }
+
+ @Deactivate
+ public void deactivate() {
+ intentManager.unregisterCompiler(PointToPointIntent.class);
+ }
+
+ @Override
+ public List<Intent> compile(PointToPointIntent intent, List<Intent> installable,
+ Set<LinkResourceAllocations> resources) {
+
+ ConnectPoint ingressPoint = intent.ingressPoint();
+ ConnectPoint egressPoint = intent.egressPoint();
+
+ if (ingressPoint.deviceId().equals(egressPoint.deviceId())) {
+ List<Link> links = asList(createEdgeLink(ingressPoint, true), createEdgeLink(egressPoint, false));
+ return asList(createPathIntent(new DefaultPath(PID, links, DEFAULT_COST), intent));
+ }
+
+ List<Link> links = new ArrayList<>();
+ Path path = getPath(intent, ingressPoint.deviceId(),
+ egressPoint.deviceId());
+
+ links.add(createEdgeLink(ingressPoint, true));
+ links.addAll(path.links());
+ links.add(createEdgeLink(egressPoint, false));
+
+ return asList(createPathIntent(new DefaultPath(PID, links, path.cost(),
+ path.annotations()), intent));
+ }
+
+ /**
+ * Creates a path intent from the specified path and original
+ * connectivity intent.
+ *
+ * @param path path to create an intent for
+ * @param intent original intent
+ */
+ private Intent createPathIntent(Path path,
+ PointToPointIntent intent) {
+ return PathIntent.builder()
+ .appId(intent.appId())
+ .selector(intent.selector())
+ .treatment(intent.treatment())
+ .path(path)
+ .constraints(intent.constraints())
+ .priority(intent.priority())
+ .build();
+ }
+
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/SinglePointToMultiPointIntentCompiler.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/SinglePointToMultiPointIntentCompiler.java
new file mode 100644
index 00000000..56565908
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/SinglePointToMultiPointIntentCompiler.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.intent.impl.compiler;
+
+import com.google.common.collect.ImmutableSet;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Link;
+import org.onosproject.net.Path;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.LinkCollectionIntent;
+import org.onosproject.net.intent.SinglePointToMultiPointIntent;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.net.resource.link.LinkResourceAllocations;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+@Component(immediate = true)
+public class SinglePointToMultiPointIntentCompiler
+ extends ConnectivityIntentCompiler<SinglePointToMultiPointIntent> {
+
+ // TODO: use off-the-shell core provider ID
+ private static final ProviderId PID =
+ new ProviderId("core", "org.onosproject.core", true);
+
+ @Activate
+ public void activate() {
+ intentManager.registerCompiler(SinglePointToMultiPointIntent.class,
+ this);
+ }
+
+ @Deactivate
+ public void deactivate() {
+ intentManager.unregisterCompiler(SinglePointToMultiPointIntent.class);
+ }
+
+
+ @Override
+ public List<Intent> compile(SinglePointToMultiPointIntent intent,
+ List<Intent> installable,
+ Set<LinkResourceAllocations> resources) {
+ Set<Link> links = new HashSet<>();
+
+ for (ConnectPoint egressPoint : intent.egressPoints()) {
+ if (egressPoint.deviceId().equals(intent.ingressPoint().deviceId())) {
+ continue;
+ }
+
+ Path path = getPath(intent, intent.ingressPoint().deviceId(), egressPoint.deviceId());
+ links.addAll(path.links());
+ }
+
+ Intent result = LinkCollectionIntent.builder()
+ .appId(intent.appId())
+ .key(intent.key())
+ .selector(intent.selector())
+ .treatment(intent.treatment())
+ .links(links)
+ .ingressPoints(ImmutableSet.of(intent.ingressPoint()))
+ .egressPoints(intent.egressPoints())
+ .priority(intent.priority())
+ .constraints(intent.constraints())
+ .build();
+
+ return Collections.singletonList(result);
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/TwoWayP2PIntentCompiler.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/TwoWayP2PIntentCompiler.java
new file mode 100644
index 00000000..50a67546
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/TwoWayP2PIntentCompiler.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.intent.impl.compiler;
+
+import com.google.common.collect.Lists;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.PointToPointIntent;
+import org.onosproject.net.intent.TwoWayP2PIntent;
+import org.onosproject.net.resource.link.LinkResourceAllocations;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A intent compiler for {@link org.onosproject.net.intent.TwoWayP2PIntent}.
+ */
+@Component(immediate = true)
+public class TwoWayP2PIntentCompiler
+ extends ConnectivityIntentCompiler<TwoWayP2PIntent> {
+
+ @Activate
+ public void activate() {
+ intentManager.registerCompiler(TwoWayP2PIntent.class, this);
+ }
+
+ @Deactivate
+ public void deactivate() {
+ intentManager.unregisterCompiler(TwoWayP2PIntent.class);
+ }
+
+ @Override
+ public List<Intent> compile(TwoWayP2PIntent intent, List<Intent> installable,
+ Set<LinkResourceAllocations> resources) {
+ return Lists.newArrayList(
+ PointToPointIntent.builder()
+ .appId(intent.appId())
+ .key(intent.key())
+ .selector(intent.selector())
+ .treatment(intent.treatment())
+ .ingressPoint(intent.one())
+ .egressPoint(intent.two())
+ .constraints(intent.constraints())
+ .priority(intent.priority())
+ .build(),
+ PointToPointIntent.builder()
+ .appId(intent.appId())
+ .key(intent.key())
+ .selector(intent.selector())
+ .treatment(intent.treatment())
+ .ingressPoint(intent.two())
+ .egressPoint(intent.one())
+ .constraints(intent.constraints())
+ .priority(intent.priority())
+ .build());
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/package-info.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/package-info.java
new file mode 100644
index 00000000..beaf5ed0
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/compiler/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Implementations of builtin intent compilers.
+ */
+package org.onosproject.net.intent.impl.compiler; \ No newline at end of file
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/package-info.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/package-info.java
new file mode 100644
index 00000000..8c516c64
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Core subsystem for tracking high-level intents for treatment of selected
+ * network traffic.
+ */
+package org.onosproject.net.intent.impl;
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/Compiling.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/Compiling.java
new file mode 100644
index 00000000..5078b5dc
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/Compiling.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.intent.impl.phase;
+
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.IntentException;
+import org.onosproject.net.intent.impl.IntentProcessor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.Optional;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Represents a phase where an intent is being compiled or recompiled.
+ */
+class Compiling implements IntentProcessPhase {
+
+ private static final Logger log = LoggerFactory.getLogger(Compiling.class);
+
+ private final IntentProcessor processor;
+ private final IntentData data;
+ private final Optional<IntentData> stored;
+
+ /**
+ * Creates a intent recompiling phase.
+ *
+ * @param processor intent processor that does work for recompiling
+ * @param data intent data containing an intent to be recompiled
+ * @param stored intent data stored in the store
+ */
+ Compiling(IntentProcessor processor, IntentData data, Optional<IntentData> stored) {
+ this.processor = checkNotNull(processor);
+ this.data = checkNotNull(data);
+ this.stored = checkNotNull(stored);
+ }
+
+ @Override
+ public Optional<IntentProcessPhase> execute() {
+ try {
+ List<Intent> compiled = processor.compile(data.intent(),
+ //TODO consider passing an optional here in the future
+ stored.isPresent() ? stored.get().installables() : null);
+ data.setInstallables(compiled);
+ return Optional.of(new Installing(processor, data, stored));
+ } catch (IntentException e) {
+ log.debug("Unable to compile intent {} due to: {}", data.intent(), e);
+ if (stored.isPresent() && !stored.get().installables().isEmpty()) {
+ // removing orphaned flows and deallocating resources
+ data.setInstallables(stored.get().installables());
+ return Optional.of(new Withdrawing(processor, data));
+ } else {
+ return Optional.of(new Failed(data));
+ }
+ }
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/Corrupt.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/Corrupt.java
new file mode 100644
index 00000000..2fbe1641
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/Corrupt.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.intent.impl.phase;
+
+import org.onosproject.net.intent.IntentData;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.intent.IntentState.CORRUPT;
+
+/**
+ * A class representing errors removing or installing intents.
+ */
+public class Corrupt extends FinalIntentProcessPhase {
+
+ private final IntentData intentData;
+
+ /**
+ * Create an instance with the specified data.
+ *
+ * @param intentData intentData
+ */
+ Corrupt(IntentData intentData) {
+ this.intentData = checkNotNull(intentData);
+ this.intentData.setState(CORRUPT);
+ }
+
+ @Override
+ public IntentData data() {
+ return intentData;
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/Failed.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/Failed.java
new file mode 100644
index 00000000..7f628e3c
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/Failed.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.intent.impl.phase;
+
+import org.onosproject.net.intent.IntentData;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.intent.IntentState.FAILED;
+
+/**
+ * Represents a phase where the compile has failed.
+ */
+public class Failed extends FinalIntentProcessPhase {
+
+ private final IntentData intentData;
+
+ /**
+ * Create an instance with the specified data.
+ *
+ * @param intentData intentData
+ */
+ Failed(IntentData intentData) {
+ this.intentData = checkNotNull(intentData);
+ this.intentData.setState(FAILED);
+ }
+
+ @Override
+ public IntentData data() {
+ return intentData;
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/FinalIntentProcessPhase.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/FinalIntentProcessPhase.java
new file mode 100644
index 00000000..c67b93b5
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/FinalIntentProcessPhase.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.intent.impl.phase;
+
+import org.onosproject.net.intent.IntentData;
+
+import java.util.Optional;
+
+/**
+ * Represents a final phase of processing an intent.
+ */
+public abstract class FinalIntentProcessPhase implements IntentProcessPhase {
+
+ @Override
+ public final Optional<IntentProcessPhase> execute() {
+ preExecute();
+ return Optional.empty();
+ }
+
+ /**
+ * Executes operations that must take place before the phase starts.
+ */
+ protected void preExecute() {}
+
+ /**
+ * Returns the IntentData object being acted on by this phase.
+ *
+ * @return intent data object for the phase
+ */
+ public abstract IntentData data();
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/InstallRequest.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/InstallRequest.java
new file mode 100644
index 00000000..a75d7cc8
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/InstallRequest.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.intent.impl.phase;
+
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.impl.IntentProcessor;
+
+import java.util.Optional;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.intent.impl.phase.IntentProcessPhase.transferErrorCount;
+
+/**
+ * Represents a phase where intent installation has been requested.
+ */
+final class InstallRequest implements IntentProcessPhase {
+
+ private final IntentProcessor processor;
+ private final IntentData data;
+ private final Optional<IntentData> stored;
+
+ /**
+ * Creates an install request phase.
+ *
+ * @param processor intent processor to be passed to intent process phases
+ * generated after this phase
+ * @param intentData intent data to be processed
+ * @param stored intent data stored in the store
+ */
+ InstallRequest(IntentProcessor processor, IntentData intentData, Optional<IntentData> stored) {
+ this.processor = checkNotNull(processor);
+ this.data = checkNotNull(intentData);
+ this.stored = checkNotNull(stored);
+ }
+
+ @Override
+ public Optional<IntentProcessPhase> execute() {
+ transferErrorCount(data, stored);
+
+ return Optional.of(new Compiling(processor, data, stored));
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/Installing.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/Installing.java
new file mode 100644
index 00000000..2ff7ca8b
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/Installing.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.intent.impl.phase;
+
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.impl.IntentProcessor;
+
+import java.util.Optional;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.intent.IntentState.INSTALLING;
+
+/**
+ * Represents a phase where an intent is being installed.
+ */
+class Installing extends FinalIntentProcessPhase {
+
+ private final IntentProcessor processor;
+ private final IntentData data;
+ private final Optional<IntentData> stored;
+
+ /**
+ * Create an installing phase.
+ *
+ * @param processor intent processor that does work for installing
+ * @param data intent data containing an intent to be installed
+ * @param stored intent data already stored
+ */
+ Installing(IntentProcessor processor, IntentData data, Optional<IntentData> stored) {
+ this.processor = checkNotNull(processor);
+ this.data = checkNotNull(data);
+ this.stored = checkNotNull(stored);
+ this.data.setState(INSTALLING);
+ }
+
+ @Override
+ public void preExecute() {
+ processor.apply(stored, Optional.of(data));
+ }
+
+ @Override
+ public IntentData data() {
+ return data;
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/IntentProcessPhase.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/IntentProcessPhase.java
new file mode 100644
index 00000000..bce572c6
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/IntentProcessPhase.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.intent.impl.phase;
+
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.impl.IntentProcessor;
+
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * Represents a phase of processing an intent.
+ */
+public interface IntentProcessPhase {
+
+ /**
+ * Execute the procedure represented by the instance
+ * and generates the next update instance.
+ *
+ * @return next update
+ */
+ Optional<IntentProcessPhase> execute();
+
+ /**
+ * Create a starting intent process phase according to intent data this class holds.
+ *
+ * @param processor intent processor to be passed to intent process phases
+ * generated while this instance is working
+ * @param data intent data to be processed
+ * @param current intent date that is stored in the store
+ * @return starting intent process phase
+ */
+ static IntentProcessPhase newInitialPhase(IntentProcessor processor,
+ IntentData data, IntentData current) {
+ switch (data.request()) {
+ case INSTALL_REQ:
+ return new InstallRequest(processor, data, Optional.ofNullable(current));
+ case WITHDRAW_REQ:
+ return new WithdrawRequest(processor, data, Optional.ofNullable(current));
+ case PURGE_REQ:
+ return new PurgeRequest(data, Optional.ofNullable(current));
+ default:
+ // illegal state
+ return new Failed(data);
+ }
+ }
+
+ static void transferErrorCount(IntentData data, Optional<IntentData> stored) {
+ if (stored.isPresent()) {
+ IntentData storedData = stored.get();
+ if (Objects.equals(data.intent(), storedData.intent()) &&
+ Objects.equals(data.request(), storedData.request())) {
+ data.setErrorCount(storedData.errorCount());
+ } else {
+ data.setErrorCount(0);
+ }
+ }
+ }
+
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/IntentWorker.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/IntentWorker.java
new file mode 100644
index 00000000..9ddcf40e
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/IntentWorker.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.intent.impl.phase;
+
+
+import java.util.Optional;
+import java.util.concurrent.Callable;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Worker to process a submitted intent. {@link #call()} method generates
+ */
+public final class IntentWorker implements Callable<FinalIntentProcessPhase> {
+
+ private final IntentProcessPhase initial;
+
+ /**
+ * Create an instance with the specified arguments.
+ *
+ * @param initial initial intent process phase
+ */
+ public IntentWorker(IntentProcessPhase initial) {
+ this.initial = checkNotNull(initial);
+ }
+
+ @Override
+ public FinalIntentProcessPhase call() throws Exception {
+ IntentProcessPhase update = initial;
+ Optional<IntentProcessPhase> currentPhase = Optional.of(update);
+ IntentProcessPhase previousPhase = update;
+
+ while (currentPhase.isPresent()) {
+ previousPhase = currentPhase.get();
+ currentPhase = previousPhase.execute();
+ }
+ return (FinalIntentProcessPhase) previousPhase;
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/PurgeRequest.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/PurgeRequest.java
new file mode 100644
index 00000000..69126dfb
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/PurgeRequest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.intent.impl.phase;
+
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.IntentState;
+import org.slf4j.Logger;
+
+import java.util.Optional;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Represents a phase of requesting a purge of an intent.
+ * <p>
+ * Note: The purge will only succeed if the intent is FAILED or WITHDRAWN.
+ * </p>
+ */
+final class PurgeRequest extends FinalIntentProcessPhase {
+
+ private static final Logger log = getLogger(PurgeRequest.class);
+
+ private final IntentData data;
+ private final Optional<IntentData> stored;
+
+ PurgeRequest(IntentData intentData, Optional<IntentData> stored) {
+ this.data = checkNotNull(intentData);
+ this.stored = checkNotNull(stored);
+ }
+
+ private boolean shouldAcceptPurge() {
+ if (!stored.isPresent()) {
+ log.info("Purge for intent {}, but intent is not present",
+ data.key());
+ return true;
+ }
+
+ IntentData storedData = stored.get();
+ if (storedData.state() == IntentState.WITHDRAWN
+ || storedData.state() == IntentState.FAILED) {
+ return true;
+ }
+ log.info("Purge for intent {} is rejected because intent state is {}",
+ data.key(), storedData.state());
+ return false;
+ }
+
+ @Override
+ public IntentData data() {
+ if (shouldAcceptPurge()) {
+ return data;
+ } else {
+ return stored.get();
+ }
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/WithdrawRequest.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/WithdrawRequest.java
new file mode 100644
index 00000000..8a0709e6
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/WithdrawRequest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.intent.impl.phase;
+
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.impl.IntentProcessor;
+
+import java.util.Optional;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.intent.impl.phase.IntentProcessPhase.transferErrorCount;
+
+/**
+ * Represents a phase of requesting a withdraw of an intent.
+ */
+final class WithdrawRequest implements IntentProcessPhase {
+
+ private final IntentProcessor processor;
+ private final IntentData data;
+ private final Optional<IntentData> stored;
+
+ /**
+ * Creates a withdraw request phase.
+ *
+ * @param processor intent processor to be passed to intent process phases
+ * generated after this phase
+ * @param intentData intent data to be processed
+ * @param stored intent data stored in the store
+ */
+ WithdrawRequest(IntentProcessor processor, IntentData intentData, Optional<IntentData> stored) {
+ this.processor = checkNotNull(processor);
+ this.data = checkNotNull(intentData);
+ this.stored = checkNotNull(stored);
+ }
+
+ @Override
+ public Optional<IntentProcessPhase> execute() {
+ //TODO perhaps we want to validate that the pending and current are the
+ // same version i.e. they are the same
+ // Note: this call is not just the symmetric version of submit
+
+ transferErrorCount(data, stored);
+
+ if (!stored.isPresent() || stored.get().installables().isEmpty()) {
+ switch (data.request()) {
+ case INSTALL_REQ:
+ return Optional.of(new Failed(data));
+ case WITHDRAW_REQ:
+ default: //TODO "default" case should not happen
+ return Optional.of(new Withdrawn(data));
+ }
+ }
+
+ data.setInstallables(stored.get().installables());
+ return Optional.of(new Withdrawing(processor, data));
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/Withdrawing.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/Withdrawing.java
new file mode 100644
index 00000000..29bc4711
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/Withdrawing.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.intent.impl.phase;
+
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.impl.IntentProcessor;
+
+import java.util.Optional;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.intent.IntentState.WITHDRAWING;
+
+/**
+ * Represents a phase where an intent is withdrawing.
+ */
+class Withdrawing extends FinalIntentProcessPhase {
+
+ private final IntentProcessor processor;
+ private final IntentData data;
+
+ /**
+ * Creates a withdrawing phase.
+ *
+ * @param processor intent processor that does work for withdrawing
+ * @param data intent data containing an intent to be withdrawn
+ */
+ Withdrawing(IntentProcessor processor, IntentData data) {
+ this.processor = checkNotNull(processor);
+ this.data = checkNotNull(data);
+ this.data.setState(WITHDRAWING);
+ }
+
+ @Override
+ protected void preExecute() {
+ processor.apply(Optional.of(data), Optional.empty());
+ }
+
+ @Override
+ public IntentData data() {
+ return data;
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/Withdrawn.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/Withdrawn.java
new file mode 100644
index 00000000..264f74c6
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/Withdrawn.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.intent.impl.phase;
+
+import org.onosproject.net.intent.IntentData;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.intent.IntentState.WITHDRAWN;
+
+/**
+ * Represents a phase where an intent has been withdrawn.
+ */
+final class Withdrawn extends FinalIntentProcessPhase {
+
+ private final IntentData data;
+
+ /**
+ * Create a withdrawn phase.
+ *
+ * @param data intent data containing an intent to be withdrawn
+ */
+ Withdrawn(IntentData data) {
+ this.data = checkNotNull(data);
+ this.data.setState(WITHDRAWN);
+ }
+
+ @Override
+ public IntentData data() {
+ return data;
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/package-info.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/package-info.java
new file mode 100644
index 00000000..56e54308
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/intent/impl/phase/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Implementations of various intent processing phases.
+ */
+package org.onosproject.net.intent.impl.phase; \ No newline at end of file
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/link/impl/BasicLinkOperator.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/link/impl/BasicLinkOperator.java
new file mode 100644
index 00000000..a6b08f62
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/link/impl/BasicLinkOperator.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.link.impl;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.time.Duration;
+
+import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.config.ConfigOperator;
+import org.onosproject.net.config.basics.BasicLinkConfig;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.Link;
+import org.onosproject.net.SparseAnnotations;
+import org.onosproject.net.link.DefaultLinkDescription;
+import org.onosproject.net.link.LinkDescription;
+import org.slf4j.Logger;
+
+/**
+ * Implementations of merge policies for various sources of link configuration
+ * information. This includes applications, provides, and network configurations.
+ */
+public final class BasicLinkOperator implements ConfigOperator {
+
+ private static final long DEF_BANDWIDTH = -1L;
+ private static final Duration DEF_DURATION = Duration.ofNanos(-1L);
+ private static final Logger log = getLogger(BasicLinkOperator.class);
+
+ private BasicLinkOperator() {
+ }
+
+ /**
+ * Generates a LinkDescription containing fields from a LinkDescription and
+ * a LinkConfig.
+ *
+ * @param cfg the link config entity from network config
+ * @param descr a LinkDescription
+ * @return LinkDescription based on both sources
+ */
+ public static LinkDescription combine(BasicLinkConfig cfg, LinkDescription descr) {
+ if (cfg == null) {
+ return descr;
+ }
+
+ // cfg.type() defaults to DIRECT, so there is a risk of unwanted override.
+ // do we want this behavior?
+ Link.Type type = descr.type();
+ if (cfg.type() != type) {
+ type = cfg.type();
+ }
+
+ SparseAnnotations sa = combine(cfg, descr.annotations());
+ return new DefaultLinkDescription(descr.src(), descr.dst(), type, sa);
+ }
+
+ /**
+ * Generates an annotation from an existing annotation and LinkConfig.
+ *
+ * @param cfg the link config entity from network config
+ * @param an the annotation
+ * @return annotation combining both sources
+ */
+ public static SparseAnnotations combine(BasicLinkConfig cfg, SparseAnnotations an) {
+ DefaultAnnotations.Builder b = DefaultAnnotations.builder();
+ if (cfg.latency() != DEF_DURATION) {
+ b.set(AnnotationKeys.LATENCY, cfg.latency().toString());
+ }
+ if (cfg.bandwidth() != DEF_BANDWIDTH) {
+ b.set(AnnotationKeys.BANDWIDTH, String.valueOf(cfg.bandwidth()));
+ }
+ return DefaultAnnotations.union(an, b.build());
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/link/impl/LinkManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/link/impl/LinkManager.java
new file mode 100644
index 00000000..157288a4
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/link/impl/LinkManager.java
@@ -0,0 +1,343 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.link.impl;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.Sets;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.net.provider.AbstractListenerProviderRegistry;
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.onosproject.net.config.NetworkConfigListener;
+import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.net.config.basics.BasicLinkConfig;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.Link.State;
+import org.onosproject.net.LinkKey;
+import org.onosproject.net.MastershipRole;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.link.LinkAdminService;
+import org.onosproject.net.link.LinkDescription;
+import org.onosproject.net.link.LinkEvent;
+import org.onosproject.net.link.LinkListener;
+import org.onosproject.net.link.LinkProvider;
+import org.onosproject.net.link.LinkProviderRegistry;
+import org.onosproject.net.link.LinkProviderService;
+import org.onosproject.net.link.LinkService;
+import org.onosproject.net.link.LinkStore;
+import org.onosproject.net.link.LinkStoreDelegate;
+import org.onosproject.net.provider.AbstractProviderService;
+import org.slf4j.Logger;
+
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static org.onosproject.net.LinkKey.linkKey;
+import static org.onosproject.security.AppGuard.checkPermission;
+import static org.slf4j.LoggerFactory.getLogger;
+import static org.onosproject.security.AppPermission.Type.*;
+
+
+/**
+ * Provides basic implementation of the link SB &amp; NB APIs.
+ */
+@Component(immediate = true)
+@Service
+public class LinkManager
+ extends AbstractListenerProviderRegistry<LinkEvent, LinkListener, LinkProvider, LinkProviderService>
+ implements LinkService, LinkAdminService, LinkProviderRegistry {
+
+ private static final String DEVICE_ID_NULL = "Device ID cannot be null";
+ private static final String LINK_DESC_NULL = "Link description cannot be null";
+ private static final String CONNECT_POINT_NULL = "Connection point cannot be null";
+
+ private final Logger log = getLogger(getClass());
+
+ private final LinkStoreDelegate delegate = new InternalStoreDelegate();
+
+ private final DeviceListener deviceListener = new InternalDeviceListener();
+
+ private final NetworkConfigListener networkConfigListener = new InternalNetworkConfigListener();
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LinkStore store;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected NetworkConfigService networkConfigService;
+
+ @Activate
+ public void activate() {
+ store.setDelegate(delegate);
+ eventDispatcher.addSink(LinkEvent.class, listenerRegistry);
+ deviceService.addListener(deviceListener);
+ networkConfigService.addListener(networkConfigListener);
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ store.unsetDelegate(delegate);
+ eventDispatcher.removeSink(LinkEvent.class);
+ deviceService.removeListener(deviceListener);
+ networkConfigService.removeListener(networkConfigListener);
+ log.info("Stopped");
+ }
+
+ @Override
+ public int getLinkCount() {
+ checkPermission(LINK_READ);
+ return store.getLinkCount();
+ }
+
+ @Override
+ public Iterable<Link> getLinks() {
+ checkPermission(LINK_READ);
+ return store.getLinks();
+ }
+
+ @Override
+ public Iterable<Link> getActiveLinks() {
+ checkPermission(LINK_READ);
+ return FluentIterable.from(getLinks())
+ .filter(new Predicate<Link>() {
+
+ @Override
+ public boolean apply(Link input) {
+ return input.state() == State.ACTIVE;
+ }
+ });
+ }
+
+ @Override
+ public Set<Link> getDeviceLinks(DeviceId deviceId) {
+ checkPermission(LINK_READ);
+ checkNotNull(deviceId, DEVICE_ID_NULL);
+ return Sets.union(store.getDeviceEgressLinks(deviceId),
+ store.getDeviceIngressLinks(deviceId));
+ }
+
+ @Override
+ public Set<Link> getDeviceEgressLinks(DeviceId deviceId) {
+ checkPermission(LINK_READ);
+ checkNotNull(deviceId, DEVICE_ID_NULL);
+ return store.getDeviceEgressLinks(deviceId);
+ }
+
+ @Override
+ public Set<Link> getDeviceIngressLinks(DeviceId deviceId) {
+ checkPermission(LINK_READ);
+ checkNotNull(deviceId, DEVICE_ID_NULL);
+ return store.getDeviceIngressLinks(deviceId);
+ }
+
+ @Override
+ public Set<Link> getLinks(ConnectPoint connectPoint) {
+ checkPermission(LINK_READ);
+ checkNotNull(connectPoint, CONNECT_POINT_NULL);
+ return Sets.union(store.getEgressLinks(connectPoint),
+ store.getIngressLinks(connectPoint));
+ }
+
+ @Override
+ public Set<Link> getEgressLinks(ConnectPoint connectPoint) {
+ checkPermission(LINK_READ);
+ checkNotNull(connectPoint, CONNECT_POINT_NULL);
+ return store.getEgressLinks(connectPoint);
+ }
+
+ @Override
+ public Set<Link> getIngressLinks(ConnectPoint connectPoint) {
+ checkPermission(LINK_READ);
+ checkNotNull(connectPoint, CONNECT_POINT_NULL);
+ return store.getIngressLinks(connectPoint);
+ }
+
+ @Override
+ public Link getLink(ConnectPoint src, ConnectPoint dst) {
+ checkPermission(LINK_READ);
+ checkNotNull(src, CONNECT_POINT_NULL);
+ checkNotNull(dst, CONNECT_POINT_NULL);
+ return store.getLink(src, dst);
+ }
+
+ @Override
+ public void removeLinks(ConnectPoint connectPoint) {
+ if (deviceService.getRole(connectPoint.deviceId()) != MastershipRole.MASTER) {
+ return;
+ }
+ removeLinks(getLinks(connectPoint), false);
+ }
+
+ @Override
+ public void removeLinks(DeviceId deviceId) {
+ if (deviceService.getRole(deviceId) != MastershipRole.MASTER) {
+ return;
+ }
+ removeLinks(getDeviceLinks(deviceId), false);
+ }
+
+ @Override
+ public void removeLink(ConnectPoint src, ConnectPoint dst) {
+ post(store.removeLink(src, dst));
+ }
+
+ // Auxiliary interceptor for device remove events to prune links that
+ // are associated with the removed device or its port.
+ private class InternalDeviceListener implements DeviceListener {
+ @Override
+ public void event(DeviceEvent event) {
+ if (event.type() == DeviceEvent.Type.DEVICE_REMOVED) {
+ removeLinks(event.subject().id());
+ } else if (event.type() == DeviceEvent.Type.PORT_REMOVED) {
+ removeLinks(new ConnectPoint(event.subject().id(),
+ event.port().number()));
+ }
+ }
+ }
+
+ @Override
+ protected LinkProviderService createProviderService(LinkProvider provider) {
+ return new InternalLinkProviderService(provider);
+ }
+
+ // Personalized link provider service issued to the supplied provider.
+ private class InternalLinkProviderService
+ extends AbstractProviderService<LinkProvider>
+ implements LinkProviderService {
+
+ InternalLinkProviderService(LinkProvider provider) {
+ super(provider);
+ }
+
+ @Override
+ public void linkDetected(LinkDescription linkDescription) {
+ checkNotNull(linkDescription, LINK_DESC_NULL);
+ checkValidity();
+ linkDescription = validateLink(linkDescription);
+ LinkEvent event = store.createOrUpdateLink(provider().id(),
+ linkDescription);
+ if (event != null) {
+ log.info("Link {} detected", linkDescription);
+ post(event);
+ }
+ }
+
+ // returns a LinkDescription made from the union of the BasicLinkConfig
+ // annotations if it exists
+ private LinkDescription validateLink(LinkDescription linkDescription) {
+ // TODO Investigate whether this can be made more efficient
+ BasicLinkConfig cfg = networkConfigService.getConfig(linkKey(linkDescription.src(),
+ linkDescription.dst()),
+ BasicLinkConfig.class);
+ BasicLinkConfig cfgTwo = networkConfigService.getConfig(linkKey(linkDescription.dst(),
+ linkDescription.src()),
+ BasicLinkConfig.class);
+
+ checkState(cfg == null || cfg.isAllowed(), "Link " + linkDescription.toString() + " is not allowed");
+ checkState(cfgTwo == null || cfgTwo.isAllowed(), "Link " + linkDescription.toString() + " is not allowed");
+
+ return BasicLinkOperator.combine(cfg, linkDescription);
+ }
+
+ @Override
+ public void linkVanished(LinkDescription linkDescription) {
+ checkNotNull(linkDescription, LINK_DESC_NULL);
+ checkValidity();
+
+ ConnectPoint src = linkDescription.src();
+ ConnectPoint dst = linkDescription.dst();
+
+ LinkEvent event = store.removeOrDownLink(src, dst);
+ if (event != null) {
+ log.info("Link {} vanished", linkDescription);
+ post(event);
+ }
+ }
+
+ @Override
+ public void linksVanished(ConnectPoint connectPoint) {
+ checkNotNull(connectPoint, "Connect point cannot be null");
+ checkValidity();
+
+ log.debug("Links for connection point {} vanished", connectPoint);
+ // FIXME: This will remove links registered by other providers
+ removeLinks(getLinks(connectPoint), true);
+ }
+
+ @Override
+ public void linksVanished(DeviceId deviceId) {
+ checkNotNull(deviceId, DEVICE_ID_NULL);
+ checkValidity();
+
+ log.debug("Links for device {} vanished", deviceId);
+ removeLinks(getDeviceLinks(deviceId), true);
+ }
+ }
+
+ // Removes all links in the specified set and emits appropriate events.
+ private void removeLinks(Set<Link> links, boolean isSoftRemove) {
+ for (Link link : links) {
+ LinkEvent event = isSoftRemove ?
+ store.removeOrDownLink(link.src(), link.dst()) :
+ store.removeLink(link.src(), link.dst());
+ if (event != null) {
+ log.info("Link {} removed/vanished", event.subject());
+ post(event);
+ }
+ }
+ }
+
+ // Store delegate to re-post events emitted from the store.
+ private class InternalStoreDelegate implements LinkStoreDelegate {
+ @Override
+ public void notify(LinkEvent event) {
+ post(event);
+ }
+ }
+
+ // listens for NetworkConfigEvents of type BasicLinkConfig and removes
+ // links that the config does not allow
+ private class InternalNetworkConfigListener implements NetworkConfigListener {
+ @Override
+ public void event(NetworkConfigEvent event) {
+ if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
+ event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
+ event.configClass().equals(BasicLinkConfig.class)) {
+ log.info("Detected Link network config event {}", event.type());
+ LinkKey lk = (LinkKey) event.subject();
+ BasicLinkConfig cfg = networkConfigService.getConfig(lk, BasicLinkConfig.class);
+ if (cfg != null && !cfg.isAllowed()) {
+ log.info("Kicking out links between {} and {}", lk.src(), lk.dst());
+ removeLink(lk.src(), lk.dst());
+ removeLink(lk.dst(), lk.src());
+ }
+ }
+ }
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/link/impl/package-info.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/link/impl/package-info.java
new file mode 100644
index 00000000..4c32c3fc
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/link/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Core subsystem for tracking global inventory of infrastructure links.
+ */
+package org.onosproject.net.link.impl;
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/newresource/impl/ResourceDeviceListener.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/newresource/impl/ResourceDeviceListener.java
new file mode 100644
index 00000000..e6d92253
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/newresource/impl/ResourceDeviceListener.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.newresource.impl;
+
+import org.onosproject.net.Device;
+import org.onosproject.net.Port;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.newresource.ResourceAdminService;
+import org.onosproject.net.newresource.ResourcePath;
+
+import java.util.concurrent.ExecutorService;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * An implementation of DeviceListener registering devices as resources.
+ */
+final class ResourceDeviceListener implements DeviceListener {
+
+ private final ResourceAdminService adminService;
+ private final ExecutorService executor;
+
+ /**
+ * Creates an instance with the specified ResourceAdminService and ExecutorService.
+ *
+ * @param adminService instance invoked to register resources
+ * @param executor executor used for processing resource registration
+ */
+ ResourceDeviceListener(ResourceAdminService adminService, ExecutorService executor) {
+ this.adminService = checkNotNull(adminService);
+ this.executor = checkNotNull(executor);
+ }
+
+ @Override
+ public void event(DeviceEvent event) {
+ Device device = event.subject();
+ switch (event.type()) {
+ case DEVICE_ADDED:
+ registerDeviceResource(device);
+ break;
+ case DEVICE_REMOVED:
+ unregisterDeviceResource(device);
+ break;
+ case PORT_ADDED:
+ registerPortResource(device, event.port());
+ break;
+ case PORT_REMOVED:
+ unregisterPortResource(device, event.port());
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void registerDeviceResource(Device device) {
+ executor.submit(() -> adminService.registerResources(ResourcePath.ROOT, device.id()));
+ }
+
+ private void unregisterDeviceResource(Device device) {
+ executor.submit(() -> adminService.unregisterResources(ResourcePath.ROOT, device.id()));
+ }
+
+ private void registerPortResource(Device device, Port port) {
+ ResourcePath parent = new ResourcePath(device.id());
+ executor.submit(() -> adminService.registerResources(parent, port.number()));
+ }
+
+ private void unregisterPortResource(Device device, Port port) {
+ ResourcePath parent = new ResourcePath(device.id());
+ executor.submit(() -> adminService.unregisterResources(parent, port.number()));
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/newresource/impl/ResourceLinkListener.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/newresource/impl/ResourceLinkListener.java
new file mode 100644
index 00000000..f04c78b9
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/newresource/impl/ResourceLinkListener.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.newresource.impl;
+
+import org.onlab.packet.MplsLabel;
+import org.onlab.packet.VlanId;
+import org.onlab.util.ItemNotFoundException;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Link;
+import org.onosproject.net.LinkKey;
+import org.onosproject.net.behaviour.MplsQuery;
+import org.onosproject.net.behaviour.VlanQuery;
+import org.onosproject.net.driver.DriverHandler;
+import org.onosproject.net.driver.DriverService;
+import org.onosproject.net.link.LinkEvent;
+import org.onosproject.net.link.LinkListener;
+import org.onosproject.net.newresource.ResourceAdminService;
+import org.onosproject.net.newresource.ResourcePath;
+
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * An implementation of LinkListener registering links as resources.
+ */
+final class ResourceLinkListener implements LinkListener {
+
+ private static final int TOTAL_VLANS = 1024;
+ private static final List<VlanId> ENTIRE_VLAN_IDS = getEntireVlans();
+
+ private static final int TOTAL_MPLS_LABELS = 1048576;
+ private static final List<MplsLabel> ENTIRE_MPLS_LABELS = getEntireMplsLabels();
+
+ private final ResourceAdminService adminService;
+ private final DriverService driverService;
+ private final ExecutorService executor;
+
+ /**
+ * Creates an instance with the specified ResourceAdminService and ExecutorService.
+ *
+ * @param adminService instance invoked to register resources
+ * @param driverService driver service instance
+ * @param executor executor used for processing resource registration
+ */
+ ResourceLinkListener(ResourceAdminService adminService, DriverService driverService, ExecutorService executor) {
+ this.adminService = checkNotNull(adminService);
+ this.driverService = checkNotNull(driverService);
+ this.executor = checkNotNull(executor);
+ }
+
+ @Override
+ public void event(LinkEvent event) {
+ Link link = event.subject();
+ switch (event.type()) {
+ case LINK_ADDED:
+ registerLinkResource(link);
+ break;
+ case LINK_REMOVED:
+ unregisterLinkResource(link);
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void registerLinkResource(Link link) {
+ executor.submit(() -> {
+ // register the link
+ LinkKey linkKey = LinkKey.linkKey(link);
+ adminService.registerResources(ResourcePath.ROOT, linkKey);
+
+ ResourcePath linkPath = new ResourcePath(linkKey);
+ // register VLAN IDs against the link
+ if (isEnabled(link, this::isVlanEnabled)) {
+ adminService.registerResources(linkPath, ENTIRE_VLAN_IDS);
+ }
+
+ // register MPLS labels against the link
+ if (isEnabled(link, this::isMplsEnabled)) {
+ adminService.registerResources(linkPath, ENTIRE_MPLS_LABELS);
+ }
+ });
+ }
+
+ private void unregisterLinkResource(Link link) {
+ LinkKey linkKey = LinkKey.linkKey(link);
+ executor.submit(() -> adminService.unregisterResources(ResourcePath.ROOT, linkKey));
+ }
+
+ private boolean isEnabled(Link link, Predicate<ConnectPoint> predicate) {
+ return predicate.test(link.src()) && predicate.test(link.dst());
+ }
+
+ private boolean isVlanEnabled(ConnectPoint cp) {
+ try {
+ DriverHandler handler = driverService.createHandler(cp.deviceId());
+ if (handler == null) {
+ return false;
+ }
+
+ VlanQuery query = handler.behaviour(VlanQuery.class);
+ return query != null && query.isEnabled(cp.port());
+ } catch (ItemNotFoundException e) {
+ return false;
+ }
+ }
+
+ private boolean isMplsEnabled(ConnectPoint cp) {
+ try {
+ DriverHandler handler = driverService.createHandler(cp.deviceId());
+ if (handler == null) {
+ return false;
+ }
+
+ MplsQuery query = handler.behaviour(MplsQuery.class);
+ return query != null && query.isEnabled(cp.port());
+ } catch (ItemNotFoundException e) {
+ return false;
+ }
+ }
+
+ private static List<VlanId> getEntireVlans() {
+ return IntStream.range(0, TOTAL_VLANS)
+ .mapToObj(x -> VlanId.vlanId((short) x))
+ .collect(Collectors.toList());
+ }
+
+ private static List<MplsLabel> getEntireMplsLabels() {
+ // potentially many objects are created
+ return IntStream.range(0, TOTAL_MPLS_LABELS)
+ .mapToObj(MplsLabel::mplsLabel)
+ .collect(Collectors.toList());
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/newresource/impl/ResourceManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/newresource/impl/ResourceManager.java
new file mode 100644
index 00000000..2cd1a2e0
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/newresource/impl/ResourceManager.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.newresource.impl;
+
+import com.google.common.annotations.Beta;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.net.newresource.ResourceAdminService;
+import org.onosproject.net.newresource.ResourceAllocation;
+import org.onosproject.net.newresource.ResourceConsumer;
+import org.onosproject.net.newresource.ResourceService;
+import org.onosproject.net.newresource.ResourcePath;
+import org.onosproject.net.newresource.ResourceStore;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * An implementation of ResourceService.
+ */
+@Component(immediate = true, enabled = false)
+@Service
+@Beta
+public final class ResourceManager implements ResourceService, ResourceAdminService {
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ResourceStore store;
+
+ @Override
+ public List<ResourceAllocation> allocate(ResourceConsumer consumer,
+ List<ResourcePath> resources) {
+ checkNotNull(consumer);
+ checkNotNull(resources);
+
+ // TODO: implement support of resource hierarchy
+ // allocation for a particular resource implies allocations for all of the sub-resources need to be done
+
+ boolean success = store.allocate(resources, consumer);
+ if (!success) {
+ return ImmutableList.of();
+ }
+
+ return resources.stream()
+ .map(x -> new ResourceAllocation(x, consumer))
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public boolean release(List<ResourceAllocation> allocations) {
+ checkNotNull(allocations);
+
+ List<ResourcePath> resources = allocations.stream()
+ .map(ResourceAllocation::resource)
+ .collect(Collectors.toList());
+ List<ResourceConsumer> consumers = allocations.stream()
+ .map(ResourceAllocation::consumer)
+ .collect(Collectors.toList());
+
+ return store.release(resources, consumers);
+ }
+
+ @Override
+ public boolean release(ResourceConsumer consumer) {
+ checkNotNull(consumer);
+
+ Collection<ResourceAllocation> allocations = getResourceAllocations(consumer);
+ return release(ImmutableList.copyOf(allocations));
+ }
+
+ @Override
+ public <T> Collection<ResourceAllocation> getResourceAllocations(ResourcePath parent, Class<T> cls) {
+ checkNotNull(parent);
+ checkNotNull(cls);
+
+ Collection<ResourcePath> resources = store.getAllocatedResources(parent, cls);
+ List<ResourceAllocation> allocations = new ArrayList<>(resources.size());
+ for (ResourcePath resource: resources) {
+ // We access store twice in this method, then the store may be updated by others
+ Optional<ResourceConsumer> consumer = store.getConsumer(resource);
+ if (consumer.isPresent()) {
+ allocations.add(new ResourceAllocation(resource, consumer.get()));
+ }
+ }
+
+ return allocations;
+ }
+
+ @Override
+ public Collection<ResourceAllocation> getResourceAllocations(ResourceConsumer consumer) {
+ checkNotNull(consumer);
+
+ Collection<ResourcePath> resources = store.getResources(consumer);
+ return resources.stream()
+ .map(x -> new ResourceAllocation(x, consumer))
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public boolean isAvailable(ResourcePath resource) {
+ checkNotNull(resource);
+
+ Optional<ResourceConsumer> consumer = store.getConsumer(resource);
+ return !consumer.isPresent();
+ }
+
+ @Override
+ public <T> boolean registerResources(ResourcePath parent, List<T> children) {
+ checkNotNull(parent);
+ checkNotNull(children);
+ checkArgument(!children.isEmpty());
+
+ List<ResourcePath> resources = Lists.transform(children, x -> ResourcePath.child(parent, x));
+ return store.register(resources);
+ }
+
+ @Override
+ public <T> boolean unregisterResources(ResourcePath parent, List<T> children) {
+ checkNotNull(parent);
+ checkNotNull(children);
+ checkArgument(!children.isEmpty());
+
+ List<ResourcePath> resources = Lists.transform(children, x -> ResourcePath.child(parent, x));
+ return store.unregister(resources);
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/newresource/impl/ResourceRegistrar.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/newresource/impl/ResourceRegistrar.java
new file mode 100644
index 00000000..4067d017
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/newresource/impl/ResourceRegistrar.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.newresource.impl;
+
+import com.google.common.annotations.Beta;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.driver.DriverService;
+import org.onosproject.net.link.LinkListener;
+import org.onosproject.net.link.LinkService;
+import org.onosproject.net.newresource.ResourceAdminService;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import static org.onlab.util.Tools.groupedThreads;
+
+/**
+ * A class registering resources when they are detected.
+ */
+@Component(immediate = true, enabled = false)
+@Beta
+public final class ResourceRegistrar {
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ResourceAdminService adminService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DriverService driverService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LinkService linkService;
+
+ private DeviceListener deviceListener;
+ private LinkListener linkListener;
+ private final ExecutorService executor =
+ Executors.newSingleThreadExecutor(groupedThreads("onos/resource", "registrar"));
+
+ @Activate
+ public void activate() {
+ deviceListener = new ResourceDeviceListener(adminService, executor);
+ deviceService.addListener(deviceListener);
+ linkListener = new ResourceLinkListener(adminService, driverService, executor);
+ linkService.addListener(linkListener);
+ }
+
+ @Deactivate
+ public void deactivate() {
+ deviceService.removeListener(deviceListener);
+ linkService.removeListener(linkListener);
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/newresource/impl/package-info.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/newresource/impl/package-info.java
new file mode 100644
index 00000000..bddfdfc1
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/newresource/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Implementation of the generic network resource subsystem.
+ */
+package org.onosproject.net.newresource.impl; \ No newline at end of file
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/packet/impl/PacketManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/packet/impl/PacketManager.java
new file mode 100644
index 00000000..75239fdd
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/packet/impl/PacketManager.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.packet.impl;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.Device;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flowobjective.DefaultForwardingObjective;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.net.flowobjective.Objective;
+import org.onosproject.net.flowobjective.ObjectiveContext;
+import org.onosproject.net.flowobjective.ObjectiveError;
+import org.onosproject.net.packet.DefaultPacketRequest;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketEvent;
+import org.onosproject.net.packet.PacketPriority;
+import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.packet.PacketProvider;
+import org.onosproject.net.packet.PacketProviderRegistry;
+import org.onosproject.net.packet.PacketProviderService;
+import org.onosproject.net.packet.PacketRequest;
+import org.onosproject.net.packet.PacketService;
+import org.onosproject.net.packet.PacketStore;
+import org.onosproject.net.packet.PacketStoreDelegate;
+import org.onosproject.net.provider.AbstractProviderRegistry;
+import org.onosproject.net.provider.AbstractProviderService;
+import org.slf4j.Logger;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.security.AppGuard.checkPermission;
+import static org.slf4j.LoggerFactory.getLogger;
+import static org.onosproject.security.AppPermission.Type.*;
+
+/**
+ * Provides a basic implementation of the packet SB &amp; NB APIs.
+ */
+@Component(immediate = true)
+@Service
+public class PacketManager
+ extends AbstractProviderRegistry<PacketProvider, PacketProviderService>
+ implements PacketService, PacketProviderRegistry {
+
+ private final Logger log = getLogger(getClass());
+
+ private static final String TABLE_TYPE_MSG =
+ "Table Type cannot be null. For requesting packets without " +
+ "table hints, use other methods in the packetService API";
+
+ private final PacketStoreDelegate delegate = new InternalStoreDelegate();
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ private CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ private DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ private FlowRuleService flowService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ private PacketStore store;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ private FlowObjectiveService objectiveService;
+
+ private ExecutorService eventHandlingExecutor;
+
+ private final DeviceListener deviceListener = new InternalDeviceListener();
+
+ private final Map<Integer, PacketProcessor> processors = new ConcurrentHashMap<>();
+
+ private ApplicationId appId;
+
+ @Activate
+ public void activate() {
+ eventHandlingExecutor = Executors.newSingleThreadExecutor(
+ groupedThreads("onos/net/packet", "event-handler"));
+ appId = coreService.getAppId(CoreService.CORE_APP_NAME);
+ store.setDelegate(delegate);
+ deviceService.addListener(deviceListener);
+ // TODO: Should we request packets for all existing devices? I believe we should.
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ store.unsetDelegate(delegate);
+ deviceService.removeListener(deviceListener);
+ eventHandlingExecutor.shutdown();
+ log.info("Stopped");
+ }
+
+ @Override
+ public void addProcessor(PacketProcessor processor, int priority) {
+ checkPermission(PACKET_EVENT);
+ checkNotNull(processor, "Processor cannot be null");
+ processors.put(priority, processor);
+ }
+
+ @Override
+ public void removeProcessor(PacketProcessor processor) {
+ checkPermission(PACKET_EVENT);
+ checkNotNull(processor, "Processor cannot be null");
+ processors.values().remove(processor);
+ }
+
+ @Override
+ public void requestPackets(TrafficSelector selector, PacketPriority priority,
+ ApplicationId appId) {
+ checkPermission(PACKET_READ);
+ checkNotNull(selector, "Selector cannot be null");
+ checkNotNull(appId, "Application ID cannot be null");
+
+ PacketRequest request = new DefaultPacketRequest(selector, priority, appId);
+ if (store.requestPackets(request)) {
+ pushToAllDevices(request);
+ }
+ }
+
+ @Override
+ public void cancelPackets(TrafficSelector selector, PacketPriority priority,
+ ApplicationId appId) {
+ checkPermission(PACKET_READ);
+ checkNotNull(selector, "Selector cannot be null");
+ checkNotNull(appId, "Application ID cannot be null");
+
+ PacketRequest request = new DefaultPacketRequest(selector, priority, appId);
+ if (store.cancelPackets(request)) {
+ removeFromAllDevices(request);
+ }
+ }
+
+ /**
+ * Pushes a packet request flow rule to all devices.
+ *
+ * @param request the packet request
+ */
+ private void pushToAllDevices(PacketRequest request) {
+ log.debug("Pushing packet request {} to all devices", request);
+ for (Device device : deviceService.getDevices()) {
+ pushRule(device, request);
+ }
+ }
+
+
+ /**
+ * Removes packet request flow rule from all devices.
+ *
+ * @param request the packet request
+ */
+ private void removeFromAllDevices(PacketRequest request) {
+ for (Device device : deviceService.getDevices()) {
+ removeRule(device, request);
+ }
+ }
+
+ /**
+ * Pushes packet intercept flow rules to the device.
+ *
+ * @param device the device to push the rules to
+ * @param request the packet request
+ */
+ private void pushRule(Device device, PacketRequest request) {
+ if (!device.type().equals(Device.Type.SWITCH)) {
+ return;
+ }
+
+ ForwardingObjective forwarding = createBuilder(request)
+ .add(new ObjectiveContext() {
+ @Override
+ public void onError(Objective objective, ObjectiveError error) {
+ log.warn("Failed to install packet request {} to {}: {}",
+ request, device.id(), error);
+ }
+ });
+
+ objectiveService.forward(device.id(), forwarding);
+ }
+
+ /**
+ * Removes packet intercept flow rules from the device.
+ *
+ * @param device the device to remove the rules deom
+ * @param request the packet request
+ */
+ private void removeRule(Device device, PacketRequest request) {
+ if (!device.type().equals(Device.Type.SWITCH)) {
+ return;
+ }
+
+ ForwardingObjective forwarding = createBuilder(request)
+ .remove(new ObjectiveContext() {
+ @Override
+ public void onError(Objective objective, ObjectiveError error) {
+ log.warn("Failed to withdraw packet request {} from {}: {}",
+ request, device.id(), error);
+ }
+ });
+
+ objectiveService.forward(device.id(), forwarding);
+ }
+
+ private DefaultForwardingObjective.Builder createBuilder(PacketRequest request) {
+ return DefaultForwardingObjective.builder()
+ .withPriority(request.priority().priorityValue())
+ .withSelector(request.selector())
+ .fromApp(appId)
+ .withFlag(ForwardingObjective.Flag.VERSATILE)
+ .withTreatment(DefaultTrafficTreatment.builder().punt().build())
+ .makePermanent();
+ }
+
+ @Override
+ public void emit(OutboundPacket packet) {
+ checkPermission(PACKET_WRITE);
+ checkNotNull(packet, "Packet cannot be null");
+ store.emit(packet);
+ }
+
+ private void localEmit(OutboundPacket packet) {
+ final Device device = deviceService.getDevice(packet.sendThrough());
+
+ if (device == null) {
+ return;
+ }
+
+ PacketProvider packetProvider = getProvider(device.providerId());
+ if (packetProvider != null) {
+ packetProvider.emit(packet);
+ }
+ }
+
+ @Override
+ protected PacketProviderService createProviderService(PacketProvider provider) {
+ return new InternalPacketProviderService(provider);
+ }
+
+ // Personalized packet provider service issued to the supplied provider.
+ private class InternalPacketProviderService
+ extends AbstractProviderService<PacketProvider>
+ implements PacketProviderService {
+
+ protected InternalPacketProviderService(PacketProvider provider) {
+ super(provider);
+ }
+
+ @Override
+ public void processPacket(PacketContext context) {
+ // TODO filter packets sent to processors based on registrations
+ for (PacketProcessor processor : processors.values()) {
+ processor.process(context);
+ }
+ }
+
+ }
+
+ /**
+ * Internal callback from the packet store.
+ */
+ private class InternalStoreDelegate implements PacketStoreDelegate {
+ @Override
+ public void notify(PacketEvent event) {
+ localEmit(event.subject());
+ }
+ }
+
+ /**
+ * Internal listener for device service events.
+ */
+ private class InternalDeviceListener implements DeviceListener {
+ @Override
+ public void event(DeviceEvent event) {
+ eventHandlingExecutor.execute(() -> {
+ try {
+ Device device = event.subject();
+ switch (event.type()) {
+ case DEVICE_ADDED:
+ case DEVICE_AVAILABILITY_CHANGED:
+ if (deviceService.isAvailable(event.subject().id())) {
+ log.debug("Pushing packet requests to device {}", event.subject().id());
+ for (PacketRequest request : store.existingRequests()) {
+ pushRule(device, request);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ } catch (Exception e) {
+ log.warn("Failed to process {}", event, e);
+ }
+ });
+ }
+ }
+
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/packet/impl/package-info.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/packet/impl/package-info.java
new file mode 100644
index 00000000..f3af4850
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/packet/impl/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Core subsystem for processing inbound packets and emitting outbound packets.
+ * Processing of inbound packets is always in the local context only, but
+ * emitting outbound packets allows for cluster-wide operation.
+ */
+package org.onosproject.net.packet.impl;
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/proxyarp/impl/ProxyArpManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/proxyarp/impl/ProxyArpManager.java
new file mode 100644
index 00000000..398260ff
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/proxyarp/impl/ProxyArpManager.java
@@ -0,0 +1,447 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.proxyarp.impl;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.packet.ARP;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.ICMP6;
+import org.onlab.packet.IPv6;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip6Address;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onlab.packet.ndp.NeighborAdvertisement;
+import org.onlab.packet.ndp.NeighborDiscoveryOptions;
+import org.onlab.packet.ndp.NeighborSolicitation;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.incubator.net.intf.InterfaceService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Host;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.edge.EdgePortService;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.link.LinkService;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.InboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketService;
+import org.onosproject.net.proxyarp.ProxyArpService;
+import org.onosproject.net.proxyarp.ProxyArpStore;
+import org.slf4j.Logger;
+
+import java.nio.ByteBuffer;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.packet.VlanId.vlanId;
+import static org.onosproject.net.HostId.hostId;
+import static org.onosproject.security.AppGuard.checkPermission;
+import static org.slf4j.LoggerFactory.getLogger;
+import static org.onosproject.security.AppPermission.Type.*;
+
+
+@Component(immediate = true)
+@Service
+public class ProxyArpManager implements ProxyArpService {
+
+ private final Logger log = getLogger(getClass());
+
+ private static final String MAC_ADDR_NULL = "Mac address cannot be null.";
+ private static final String REQUEST_NULL = "ARP or NDP request cannot be null.";
+ private static final String REQUEST_NOT_ARP = "Ethernet frame does not contain ARP request.";
+ private static final String NOT_ARP_REQUEST = "ARP is not a request.";
+ private static final String NOT_ARP_REPLY = "ARP is not a reply.";
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected EdgePortService edgeService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostService hostService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PacketService packetService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LinkService linkService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ProxyArpStore store;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected InterfaceService interfaceService;
+
+ @Activate
+ public void activate() {
+ store.setDelegate(this::sendTo);
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ store.setDelegate(null);
+ log.info("Stopped");
+ }
+
+ @Override
+ public boolean isKnown(IpAddress addr) {
+ checkPermission(PACKET_READ);
+
+ checkNotNull(addr, MAC_ADDR_NULL);
+ Set<Host> hosts = hostService.getHostsByIp(addr);
+ return !hosts.isEmpty();
+ }
+
+ @Override
+ public void reply(Ethernet eth, ConnectPoint inPort) {
+ checkPermission(PACKET_WRITE);
+
+ checkNotNull(eth, REQUEST_NULL);
+
+ if (eth.getEtherType() == Ethernet.TYPE_ARP) {
+ replyArp(eth, inPort);
+ } else if (eth.getEtherType() == Ethernet.TYPE_IPV6) {
+ replyNdp(eth, inPort);
+ }
+ }
+
+ private void replyArp(Ethernet eth, ConnectPoint inPort) {
+ ARP arp = (ARP) eth.getPayload();
+ checkArgument(arp.getOpCode() == ARP.OP_REQUEST, NOT_ARP_REQUEST);
+ checkNotNull(inPort);
+ Ip4Address targetAddress = Ip4Address.valueOf(arp.getTargetProtocolAddress());
+
+ VlanId vlan = vlanId(eth.getVlanID());
+
+ if (hasIpAddress(inPort)) {
+ // If the request came from outside the network, only reply if it was
+ // for one of our external addresses.
+
+ interfaceService.getInterfacesByPort(inPort)
+ .stream()
+ .filter(intf -> intf.ipAddresses()
+ .stream()
+ .anyMatch(ia -> ia.ipAddress().equals(targetAddress)))
+ .forEach(intf -> buildAndSendArp(targetAddress, intf.mac(), eth, inPort));
+
+ // Stop here and don't proxy ARPs if the port has an IP address
+ return;
+ }
+
+ // See if we have the target host in the host store
+
+ Set<Host> hosts = hostService.getHostsByIp(targetAddress);
+
+ Host dst = null;
+ Host src = hostService.getHost(hostId(eth.getSourceMAC(),
+ vlanId(eth.getVlanID())));
+
+ for (Host host : hosts) {
+ if (host.vlan().equals(vlan)) {
+ dst = host;
+ break;
+ }
+ }
+
+ if (src != null && dst != null) {
+ // We know the target host so we can respond
+ buildAndSendArp(targetAddress, dst.mac(), eth, inPort);
+ return;
+ }
+
+ // If the source address matches one of our external addresses
+ // it could be a request from an internal host to an external
+ // address. Forward it over to the correct port.
+ Ip4Address source =
+ Ip4Address.valueOf(arp.getSenderProtocolAddress());
+
+ boolean matched = false;
+ Set<Interface> interfaces = interfaceService.getInterfacesByIp(source);
+ for (Interface intf : interfaces) {
+ if (intf.vlan().equals(vlan)) {
+ matched = true;
+ sendTo(eth, intf.connectPoint());
+ break;
+ }
+ }
+
+ if (matched) {
+ return;
+ }
+
+ // The request couldn't be resolved.
+ // Flood the request on all ports except the incoming port.
+ flood(eth, inPort);
+ }
+
+ private void replyNdp(Ethernet eth, ConnectPoint inPort) {
+ IPv6 ipv6 = (IPv6) eth.getPayload();
+ ICMP6 icmpv6 = (ICMP6) ipv6.getPayload();
+ NeighborSolicitation nsol = (NeighborSolicitation) icmpv6.getPayload();
+ Ip6Address targetAddress = Ip6Address.valueOf(nsol.getTargetAddress());
+
+ VlanId vlan = vlanId(eth.getVlanID());
+
+ // If the request came from outside the network, only reply if it was
+ // for one of our external addresses.
+ if (hasIpAddress(inPort)) {
+ interfaceService.getInterfacesByPort(inPort)
+ .stream()
+ .filter(intf -> intf.ipAddresses()
+ .stream()
+ .anyMatch(ia -> ia.ipAddress().equals(targetAddress)))
+ .forEach(intf -> buildAndSendNdp(targetAddress, intf.mac(), eth, inPort));
+ return;
+ }
+
+ // Continue with normal proxy ARP case
+
+ Set<Host> hosts = hostService.getHostsByIp(targetAddress);
+
+ Host dst = null;
+ Host src = hostService.getHost(hostId(eth.getSourceMAC(),
+ vlanId(eth.getVlanID())));
+
+ for (Host host : hosts) {
+ if (host.vlan().equals(vlan)) {
+ dst = host;
+ break;
+ }
+ }
+
+ if (src != null && dst != null) {
+ // We know the target host so we can respond
+ buildAndSendNdp(targetAddress, dst.mac(), eth, inPort);
+ return;
+ }
+
+ // If the source address matches one of our external addresses
+ // it could be a request from an internal host to an external
+ // address. Forward it over to the correct port.
+ Ip6Address source =
+ Ip6Address.valueOf(ipv6.getSourceAddress());
+
+ boolean matched = false;
+
+ Set<Interface> interfaces = interfaceService.getInterfacesByIp(source);
+ for (Interface intf : interfaces) {
+ if (intf.vlan().equals(vlan)) {
+ matched = true;
+ sendTo(eth, intf.connectPoint());
+ break;
+ }
+ }
+
+ if (matched) {
+ return;
+ }
+
+ // The request couldn't be resolved.
+ // Flood the request on all ports except the incoming ports.
+ flood(eth, inPort);
+ }
+ //TODO checkpoint
+
+ private void buildAndSendArp(Ip4Address srcIp, MacAddress srcMac,
+ Ethernet request, ConnectPoint port) {
+ sendTo(ARP.buildArpReply(srcIp, srcMac, request), port);
+ }
+
+ private void buildAndSendNdp(Ip6Address srcIp, MacAddress srcMac,
+ Ethernet request, ConnectPoint port) {
+ sendTo(buildNdpReply(srcIp, srcMac, request), port);
+ }
+
+ /**
+ * Outputs the given packet out the given port.
+ *
+ * @param packet the packet to send
+ * @param outPort the port to send it out
+ */
+ private void sendTo(Ethernet packet, ConnectPoint outPort) {
+ sendTo(outPort, ByteBuffer.wrap(packet.serialize()));
+ }
+
+ private void sendTo(ConnectPoint outPort, ByteBuffer packet) {
+ if (!edgeService.isEdgePoint(outPort)) {
+ // Sanity check to make sure we don't send the packet out an
+ // internal port and create a loop (could happen due to
+ // misconfiguration).
+ return;
+ }
+
+ TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
+ builder.setOutput(outPort.port());
+ packetService.emit(new DefaultOutboundPacket(outPort.deviceId(),
+ builder.build(), packet));
+ }
+
+ /**
+ * Returns whether the given port has any IP addresses configured or not.
+ *
+ * @param port the port to check
+ * @return true if the port has at least one IP address configured,
+ * otherwise false
+ */
+ private boolean hasIpAddress(ConnectPoint port) {
+ return interfaceService.getInterfacesByPort(port)
+ .stream()
+ .map(intf -> intf.ipAddresses())
+ .findAny()
+ .isPresent();
+ }
+
+ @Override
+ public void forward(Ethernet eth, ConnectPoint inPort) {
+ checkPermission(PACKET_WRITE);
+
+ checkNotNull(eth, REQUEST_NULL);
+
+ Host h = hostService.getHost(hostId(eth.getDestinationMAC(),
+ vlanId(eth.getVlanID())));
+
+ if (h == null) {
+ flood(eth, inPort);
+ } else {
+ Host subject = hostService.getHost(hostId(eth.getSourceMAC(),
+ vlanId(eth.getVlanID())));
+ store.forward(h.location(), subject, ByteBuffer.wrap(eth.serialize()));
+ }
+ }
+
+ @Override
+ public boolean handlePacket(PacketContext context) {
+ checkPermission(PACKET_WRITE);
+
+ InboundPacket pkt = context.inPacket();
+ Ethernet ethPkt = pkt.parsed();
+
+ if (ethPkt == null) {
+ return false;
+ }
+ if (ethPkt.getEtherType() == Ethernet.TYPE_ARP) {
+ return handleArp(context, ethPkt);
+ } else if (ethPkt.getEtherType() == Ethernet.TYPE_IPV6) {
+ return handleNdp(context, ethPkt);
+ }
+ return false;
+ }
+
+ private boolean handleArp(PacketContext context, Ethernet ethPkt) {
+ ARP arp = (ARP) ethPkt.getPayload();
+
+ if (arp.getOpCode() == ARP.OP_REPLY) {
+ forward(ethPkt, context.inPacket().receivedFrom());
+ } else if (arp.getOpCode() == ARP.OP_REQUEST) {
+ reply(ethPkt, context.inPacket().receivedFrom());
+ } else {
+ return false;
+ }
+ context.block();
+ return true;
+ }
+
+ private boolean handleNdp(PacketContext context, Ethernet ethPkt) {
+ IPv6 ipv6 = (IPv6) ethPkt.getPayload();
+
+ if (ipv6.getNextHeader() != IPv6.PROTOCOL_ICMP6) {
+ return false;
+ }
+ ICMP6 icmpv6 = (ICMP6) ipv6.getPayload();
+ if (icmpv6.getIcmpType() == ICMP6.NEIGHBOR_ADVERTISEMENT) {
+ forward(ethPkt, context.inPacket().receivedFrom());
+ } else if (icmpv6.getIcmpType() == ICMP6.NEIGHBOR_SOLICITATION) {
+ reply(ethPkt, context.inPacket().receivedFrom());
+ } else {
+ return false;
+ }
+ context.block();
+ return true;
+ }
+
+ /**
+ * Flood the arp request at all edges in the network.
+ *
+ * @param request the arp request
+ * @param inPort the connect point the arp request was received on
+ */
+ private void flood(Ethernet request, ConnectPoint inPort) {
+ TrafficTreatment.Builder builder = null;
+ ByteBuffer buf = ByteBuffer.wrap(request.serialize());
+
+ for (ConnectPoint connectPoint : edgeService.getEdgePoints()) {
+ if (hasIpAddress(connectPoint) || connectPoint.equals(inPort)) {
+ continue;
+ }
+
+ builder = DefaultTrafficTreatment.builder();
+ builder.setOutput(connectPoint.port());
+ packetService.emit(new DefaultOutboundPacket(connectPoint.deviceId(),
+ builder.build(), buf));
+ }
+ }
+
+ /**
+ * Builds an Neighbor Discovery reply based on a request.
+ *
+ * @param srcIp the IP address to use as the reply source
+ * @param srcMac the MAC address to use as the reply source
+ * @param request the Neighbor Solicitation request we got
+ * @return an Ethernet frame containing the Neighbor Advertisement reply
+ */
+ private Ethernet buildNdpReply(Ip6Address srcIp, MacAddress srcMac,
+ Ethernet request) {
+ Ethernet eth = new Ethernet();
+ eth.setDestinationMACAddress(request.getSourceMAC());
+ eth.setSourceMACAddress(srcMac);
+ eth.setEtherType(Ethernet.TYPE_IPV6);
+ eth.setVlanID(request.getVlanID());
+
+ IPv6 requestIp = (IPv6) request.getPayload();
+ IPv6 ipv6 = new IPv6();
+ ipv6.setSourceAddress(srcIp.toOctets());
+ ipv6.setDestinationAddress(requestIp.getSourceAddress());
+ ipv6.setHopLimit((byte) 255);
+
+ ICMP6 icmp6 = new ICMP6();
+ icmp6.setIcmpType(ICMP6.NEIGHBOR_ADVERTISEMENT);
+ icmp6.setIcmpCode((byte) 0);
+
+ NeighborAdvertisement nadv = new NeighborAdvertisement();
+ nadv.setTargetAddress(srcIp.toOctets());
+ nadv.setSolicitedFlag((byte) 1);
+ nadv.setOverrideFlag((byte) 1);
+ nadv.addOption(NeighborDiscoveryOptions.TYPE_TARGET_LL_ADDRESS,
+ srcMac.toBytes());
+
+ icmp6.setPayload(nadv);
+ ipv6.setPayload(icmp6);
+ eth.setPayload(ipv6);
+ return eth;
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/proxyarp/impl/package-info.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/proxyarp/impl/package-info.java
new file mode 100644
index 00000000..9ce268fb
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/proxyarp/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Core subsystem for responding to arp requests.
+ */
+package org.onosproject.net.proxyarp.impl;
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/resource/impl/DeviceResourceManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/resource/impl/DeviceResourceManager.java
new file mode 100644
index 00000000..62b4112b
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/resource/impl/DeviceResourceManager.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.resource.impl;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.net.Port;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentId;
+import org.onosproject.net.resource.device.DeviceResourceService;
+import org.onosproject.net.resource.device.DeviceResourceStore;
+import org.slf4j.Logger;
+
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provides basic implementation of device resources allocation.
+ */
+@Component(immediate = true)
+@Service
+public class DeviceResourceManager implements DeviceResourceService {
+
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ private DeviceResourceStore store;
+
+ @Activate
+ public void activate() {
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ log.info("Stopped");
+ }
+
+ @Override
+ public boolean requestPorts(Set<Port> ports, Intent intent) {
+ checkNotNull(intent);
+
+ return store.allocatePorts(ports, intent.id());
+ }
+
+ @Override
+ public Set<Port> getAllocations(IntentId intentId) {
+ return store.getAllocations(intentId);
+ }
+
+ @Override
+ public IntentId getAllocations(Port port) {
+ return store.getAllocations(port);
+ }
+
+ @Override
+ public void releaseMapping(IntentId intentId) {
+ store.releaseMapping(intentId);
+ }
+
+ @Override
+ public boolean requestMapping(IntentId keyIntentId, IntentId valIntentId) {
+ return store.allocateMapping(keyIntentId, valIntentId);
+ }
+
+ @Override
+ public Set<IntentId> getMapping(IntentId intentId) {
+ return store.getMapping(intentId);
+ }
+
+ @Override
+ public void releasePorts(IntentId intentId) {
+ store.releasePorts(intentId);
+ }
+
+ private Port getTypedPort(Set<Port> ports, Port.Type type) {
+ for (Port port : ports) {
+ if (port.type() == type) {
+ return port;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/resource/impl/LinkResourceManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/resource/impl/LinkResourceManager.java
new file mode 100644
index 00000000..8b9952ed
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/resource/impl/LinkResourceManager.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.resource.impl;
+
+import com.google.common.collect.Sets;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.event.AbstractListenerManager;
+import org.onosproject.net.Link;
+import org.onosproject.net.intent.IntentId;
+import org.onosproject.net.resource.ResourceAllocation;
+import org.onosproject.net.resource.ResourceRequest;
+import org.onosproject.net.resource.ResourceType;
+import org.onosproject.net.resource.link.BandwidthResourceAllocation;
+import org.onosproject.net.resource.link.BandwidthResourceRequest;
+import org.onosproject.net.resource.link.DefaultLinkResourceAllocations;
+import org.onosproject.net.resource.link.LambdaResource;
+import org.onosproject.net.resource.link.LambdaResourceAllocation;
+import org.onosproject.net.resource.link.LambdaResourceRequest;
+import org.onosproject.net.resource.link.LinkResourceAllocations;
+import org.onosproject.net.resource.link.LinkResourceEvent;
+import org.onosproject.net.resource.link.LinkResourceListener;
+import org.onosproject.net.resource.link.LinkResourceRequest;
+import org.onosproject.net.resource.link.LinkResourceService;
+import org.onosproject.net.resource.link.LinkResourceStore;
+import org.onosproject.net.resource.link.LinkResourceStoreDelegate;
+import org.onosproject.net.resource.link.MplsLabel;
+import org.onosproject.net.resource.link.MplsLabelResourceAllocation;
+import org.onosproject.net.resource.link.MplsLabelResourceRequest;
+import org.slf4j.Logger;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.security.AppGuard.checkPermission;
+import static org.slf4j.LoggerFactory.getLogger;
+import static org.onosproject.security.AppPermission.Type.*;
+
+
+/**
+ * Provides basic implementation of link resources allocation.
+ */
+@Component(immediate = true)
+@Service
+public class LinkResourceManager
+ extends AbstractListenerManager<LinkResourceEvent, LinkResourceListener>
+ implements LinkResourceService {
+
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ private LinkResourceStore store;
+
+ @Activate
+ public void activate() {
+ eventDispatcher.addSink(LinkResourceEvent.class, listenerRegistry);
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ eventDispatcher.removeSink(LinkResourceEvent.class);
+ log.info("Stopped");
+ }
+
+ /**
+ * Returns available lambdas on specified link.
+ *
+ * @param link the link
+ * @return available lambdas on specified link
+ */
+ private Set<LambdaResource> getAvailableLambdas(Link link) {
+ checkNotNull(link);
+ Set<ResourceAllocation> resAllocs = store.getFreeResources(link);
+ if (resAllocs == null) {
+ return Collections.emptySet();
+ }
+ Set<LambdaResource> lambdas = new HashSet<>();
+ for (ResourceAllocation res : resAllocs) {
+ if (res.type() == ResourceType.LAMBDA) {
+ lambdas.add(((LambdaResourceAllocation) res).lambda());
+ }
+ }
+ return lambdas;
+ }
+
+
+ /**
+ * Returns available lambdas on specified links.
+ *
+ * @param links the links
+ * @return available lambdas on specified links
+ */
+ private Iterable<LambdaResource> getAvailableLambdas(Iterable<Link> links) {
+ checkNotNull(links);
+ Iterator<Link> i = links.iterator();
+ checkArgument(i.hasNext());
+ Set<LambdaResource> lambdas = new HashSet<>(getAvailableLambdas(i.next()));
+ while (i.hasNext()) {
+ lambdas.retainAll(getAvailableLambdas(i.next()));
+ }
+ return lambdas;
+ }
+
+
+ /**
+ * Returns available MPLS label on specified link.
+ *
+ * @param link the link
+ * @return available MPLS labels on specified link
+ */
+ private Iterable<MplsLabel> getAvailableMplsLabels(Link link) {
+ Set<ResourceAllocation> resAllocs = store.getFreeResources(link);
+ if (resAllocs == null) {
+ return Collections.emptySet();
+ }
+ Set<MplsLabel> mplsLabels = new HashSet<>();
+ for (ResourceAllocation res : resAllocs) {
+ if (res.type() == ResourceType.MPLS_LABEL) {
+
+ mplsLabels.add(((MplsLabelResourceAllocation) res).mplsLabel());
+ }
+ }
+
+ return mplsLabels;
+ }
+
+ @Override
+ public LinkResourceAllocations requestResources(LinkResourceRequest req) {
+ checkPermission(LINK_WRITE);
+
+ // TODO Concatenate multiple bandwidth requests.
+ // TODO Support multiple lambda resource requests.
+ // TODO Throw appropriate exception.
+ Set<ResourceAllocation> allocs = new HashSet<>();
+ Map<Link, Set<ResourceAllocation>> allocsPerLink = new HashMap<>();
+ for (ResourceRequest r : req.resources()) {
+ switch (r.type()) {
+ case BANDWIDTH:
+ BandwidthResourceRequest br = (BandwidthResourceRequest) r;
+ allocs.add(new BandwidthResourceAllocation(br.bandwidth()));
+ break;
+ case LAMBDA:
+ Iterator<LambdaResource> lambdaIterator =
+ getAvailableLambdas(req.links()).iterator();
+ if (lambdaIterator.hasNext()) {
+ allocs.add(new LambdaResourceAllocation(lambdaIterator.next()));
+ } else {
+ log.info("Failed to allocate lambda resource.");
+ return null;
+ }
+ break;
+ case MPLS_LABEL:
+ for (Link link : req.links()) {
+ if (allocsPerLink.get(link) == null) {
+ allocsPerLink.put(link, new HashSet<>());
+ }
+ Iterator<MplsLabel> mplsIter = getAvailableMplsLabels(link)
+ .iterator();
+ if (mplsIter.hasNext()) {
+ allocsPerLink.get(link)
+ .add(new MplsLabelResourceAllocation(mplsIter
+ .next()));
+ } else {
+ log.info("Failed to allocate MPLS resource.");
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ Map<Link, Set<ResourceAllocation>> allocations = new HashMap<>();
+ for (Link link : req.links()) {
+ allocations.put(link, new HashSet<>(allocs));
+ Set<ResourceAllocation> linkAllocs = allocsPerLink.get(link);
+ if (linkAllocs != null) {
+ allocations.get(link).addAll(linkAllocs);
+ }
+ }
+ LinkResourceAllocations result =
+ new DefaultLinkResourceAllocations(req, allocations);
+ store.allocateResources(result);
+ return result;
+
+ }
+
+ @Override
+ public void releaseResources(LinkResourceAllocations allocations) {
+ checkPermission(LINK_WRITE);
+ final LinkResourceEvent event = store.releaseResources(allocations);
+ if (event != null) {
+ post(event);
+ }
+ }
+
+ @Override
+ public LinkResourceAllocations updateResources(LinkResourceRequest req,
+ LinkResourceAllocations oldAllocations) {
+ checkPermission(LINK_WRITE);
+ releaseResources(oldAllocations);
+ return requestResources(req);
+ }
+
+ @Override
+ public Iterable<LinkResourceAllocations> getAllocations() {
+ checkPermission(LINK_READ);
+ return store.getAllocations();
+ }
+
+ @Override
+ public Iterable<LinkResourceAllocations> getAllocations(Link link) {
+ checkPermission(LINK_READ);
+ return store.getAllocations(link);
+ }
+
+ @Override
+ public LinkResourceAllocations getAllocations(IntentId intentId) {
+ checkPermission(LINK_READ);
+ return store.getAllocations(intentId);
+ }
+
+ @Override
+ public Iterable<ResourceRequest> getAvailableResources(Link link) {
+ checkPermission(LINK_READ);
+
+ Set<ResourceAllocation> freeRes = store.getFreeResources(link);
+ Set<ResourceRequest> result = new HashSet<>();
+ for (ResourceAllocation alloc : freeRes) {
+ switch (alloc.type()) {
+ case BANDWIDTH:
+ result.add(new BandwidthResourceRequest(
+ ((BandwidthResourceAllocation) alloc).bandwidth()));
+ break;
+ case LAMBDA:
+ result.add(new LambdaResourceRequest());
+ break;
+ case MPLS_LABEL:
+ result.add(new MplsLabelResourceRequest());
+ break;
+ default:
+ break;
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public Iterable<ResourceRequest> getAvailableResources(Link link,
+ LinkResourceAllocations allocations) {
+ checkPermission(LINK_READ);
+
+ Set<ResourceAllocation> allocatedRes = allocations.getResourceAllocation(link);
+ Set<ResourceRequest> result = Sets.newHashSet(getAvailableResources(link));
+ result.removeAll(allocatedRes);
+ return result;
+ }
+
+ /**
+ * Store delegate to re-post events emitted from the store.
+ */
+ private class InternalStoreDelegate implements LinkResourceStoreDelegate {
+ @Override
+ public void notify(LinkResourceEvent event) {
+ post(event);
+ }
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/resource/impl/package-info.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/resource/impl/package-info.java
new file mode 100644
index 00000000..fd0bbdec
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/resource/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Services for reserving network resources, e.g.&nbsp;bandwidth, lambdas.
+ */
+package org.onosproject.net.resource.impl;
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/statistic/impl/StatisticManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/statistic/impl/StatisticManager.java
new file mode 100644
index 00000000..996ad14e
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/statistic/impl/StatisticManager.java
@@ -0,0 +1,379 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.statistic.impl;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.base.Predicate;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableSet;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.GroupId;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Link;
+import org.onosproject.net.Path;
+
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleEvent;
+import org.onosproject.net.flow.FlowRuleListener;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.statistic.DefaultLoad;
+import org.onosproject.net.statistic.Load;
+import org.onosproject.net.statistic.StatisticService;
+import org.onosproject.net.statistic.StatisticStore;
+import org.slf4j.Logger;
+
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+import static org.onosproject.security.AppGuard.checkPermission;
+import static org.onosproject.security.AppPermission.Type.*;
+
+
+/**
+ * Provides an implementation of the Statistic Service.
+ */
+@Component(immediate = true)
+@Service
+public class StatisticManager implements StatisticService {
+
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowRuleService flowRuleService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected StatisticStore statisticStore;
+
+
+ private final InternalFlowRuleListener listener = new InternalFlowRuleListener();
+
+ @Activate
+ public void activate() {
+ flowRuleService.addListener(listener);
+ log.info("Started");
+
+ }
+
+ @Deactivate
+ public void deactivate() {
+ flowRuleService.removeListener(listener);
+ log.info("Stopped");
+ }
+
+ @Override
+ public Load load(Link link) {
+ checkPermission(STATISTIC_READ);
+
+ return load(link.src());
+ }
+
+ @Override
+ public Load load(Link link, ApplicationId appId, Optional<GroupId> groupId) {
+ checkPermission(STATISTIC_READ);
+
+ Statistics stats = getStatistics(link.src());
+ if (!stats.isValid()) {
+ return new DefaultLoad();
+ }
+
+ ImmutableSet<FlowEntry> current = FluentIterable.from(stats.current())
+ .filter(hasApplicationId(appId))
+ .filter(hasGroupId(groupId))
+ .toSet();
+ ImmutableSet<FlowEntry> previous = FluentIterable.from(stats.previous())
+ .filter(hasApplicationId(appId))
+ .filter(hasGroupId(groupId))
+ .toSet();
+
+ return new DefaultLoad(aggregate(current), aggregate(previous));
+ }
+
+ @Override
+ public Load load(ConnectPoint connectPoint) {
+ checkPermission(STATISTIC_READ);
+
+ return loadInternal(connectPoint);
+ }
+
+ @Override
+ public Link max(Path path) {
+ checkPermission(STATISTIC_READ);
+
+ if (path.links().isEmpty()) {
+ return null;
+ }
+ Load maxLoad = new DefaultLoad();
+ Link maxLink = null;
+ for (Link link : path.links()) {
+ Load load = loadInternal(link.src());
+ if (load.rate() > maxLoad.rate()) {
+ maxLoad = load;
+ maxLink = link;
+ }
+ }
+ return maxLink;
+ }
+
+ @Override
+ public Link min(Path path) {
+ checkPermission(STATISTIC_READ);
+
+ if (path.links().isEmpty()) {
+ return null;
+ }
+ Load minLoad = new DefaultLoad();
+ Link minLink = null;
+ for (Link link : path.links()) {
+ Load load = loadInternal(link.src());
+ if (load.rate() < minLoad.rate()) {
+ minLoad = load;
+ minLink = link;
+ }
+ }
+ return minLink;
+ }
+
+ @Override
+ public FlowRule highestHitter(ConnectPoint connectPoint) {
+ checkPermission(STATISTIC_READ);
+
+ Set<FlowEntry> hitters = statisticStore.getCurrentStatistic(connectPoint);
+ if (hitters.isEmpty()) {
+ return null;
+ }
+
+ FlowEntry max = hitters.iterator().next();
+ for (FlowEntry entry : hitters) {
+ if (entry.bytes() > max.bytes()) {
+ max = entry;
+ }
+ }
+ return max;
+ }
+
+ private Load loadInternal(ConnectPoint connectPoint) {
+ Statistics stats = getStatistics(connectPoint);
+ if (!stats.isValid()) {
+ return new DefaultLoad();
+ }
+
+ return new DefaultLoad(aggregate(stats.current), aggregate(stats.previous));
+ }
+
+ /**
+ * Returns statistics of the specified port.
+ *
+ * @param connectPoint port to query
+ * @return statistics
+ */
+ private Statistics getStatistics(ConnectPoint connectPoint) {
+ Set<FlowEntry> current;
+ Set<FlowEntry> previous;
+ synchronized (statisticStore) {
+ current = getCurrentStatistic(connectPoint);
+ previous = getPreviousStatistic(connectPoint);
+ }
+
+ return new Statistics(current, previous);
+ }
+
+ /**
+ * Returns the current statistic of the specified port.
+
+ * @param connectPoint port to query
+ * @return set of flow entries
+ */
+ private Set<FlowEntry> getCurrentStatistic(ConnectPoint connectPoint) {
+ Set<FlowEntry> stats = statisticStore.getCurrentStatistic(connectPoint);
+ if (stats == null) {
+ return Collections.emptySet();
+ } else {
+ return stats;
+ }
+ }
+
+ /**
+ * Returns the previous statistic of the specified port.
+ *
+ * @param connectPoint port to query
+ * @return set of flow entries
+ */
+ private Set<FlowEntry> getPreviousStatistic(ConnectPoint connectPoint) {
+ Set<FlowEntry> stats = statisticStore.getPreviousStatistic(connectPoint);
+ if (stats == null) {
+ return Collections.emptySet();
+ } else {
+ return stats;
+ }
+ }
+
+ // TODO: make aggregation function generic by passing a function
+ // (applying Java 8 Stream API?)
+ /**
+ * Aggregates a set of values.
+ * @param values the values to aggregate
+ * @return a long value
+ */
+ private long aggregate(Set<FlowEntry> values) {
+ long sum = 0;
+ for (FlowEntry f : values) {
+ sum += f.bytes();
+ }
+ return sum;
+ }
+
+ /**
+ * Internal flow rule event listener.
+ */
+ private class InternalFlowRuleListener implements FlowRuleListener {
+
+ @Override
+ public void event(FlowRuleEvent event) {
+ FlowRule rule = event.subject();
+ switch (event.type()) {
+ case RULE_ADDED:
+ case RULE_UPDATED:
+ if (rule instanceof FlowEntry) {
+ statisticStore.addOrUpdateStatistic((FlowEntry) rule);
+ }
+ break;
+ case RULE_ADD_REQUESTED:
+ statisticStore.prepareForStatistics(rule);
+ break;
+ case RULE_REMOVE_REQUESTED:
+ statisticStore.removeFromStatistics(rule);
+ break;
+ case RULE_REMOVED:
+ break;
+ default:
+ log.warn("Unknown flow rule event {}", event);
+ }
+ }
+ }
+
+ /**
+ * Internal data class holding two set of flow entries.
+ */
+ private static class Statistics {
+ private final ImmutableSet<FlowEntry> current;
+ private final ImmutableSet<FlowEntry> previous;
+
+ public Statistics(Set<FlowEntry> current, Set<FlowEntry> previous) {
+ this.current = ImmutableSet.copyOf(checkNotNull(current));
+ this.previous = ImmutableSet.copyOf(checkNotNull(previous));
+ }
+
+ /**
+ * Returns flow entries as the current value.
+ *
+ * @return flow entries as the current value
+ */
+ public ImmutableSet<FlowEntry> current() {
+ return current;
+ }
+
+ /**
+ * Returns flow entries as the previous value.
+ *
+ * @return flow entries as the previous value
+ */
+ public ImmutableSet<FlowEntry> previous() {
+ return previous;
+ }
+
+ /**
+ * Validates values are not empty.
+ *
+ * @return false if either of the sets is empty. Otherwise, true.
+ */
+ public boolean isValid() {
+ return !(current.isEmpty() || previous.isEmpty());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(current, previous);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof Statistics)) {
+ return false;
+ }
+ final Statistics other = (Statistics) obj;
+ return Objects.equals(this.current, other.current) && Objects.equals(this.previous, other.previous);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("current", current)
+ .add("previous", previous)
+ .toString();
+ }
+ }
+
+ /**
+ * Creates a predicate that checks the application ID of a flow entry is the same as
+ * the specified application ID.
+ *
+ * @param appId application ID to be checked
+ * @return predicate
+ */
+ private static Predicate<FlowEntry> hasApplicationId(ApplicationId appId) {
+ return new Predicate<FlowEntry>() {
+ @Override
+ public boolean apply(FlowEntry flowEntry) {
+ return flowEntry.appId() == appId.id();
+ }
+ };
+ }
+
+ /**
+ * Create a predicate that checks the group ID of a flow entry is the same as
+ * the specified group ID.
+ *
+ * @param groupId group ID to be checked
+ * @return predicate
+ */
+ private static Predicate<FlowEntry> hasGroupId(Optional<GroupId> groupId) {
+ return new Predicate<FlowEntry>() {
+ @Override
+ public boolean apply(FlowEntry flowEntry) {
+ if (!groupId.isPresent()) {
+ return false;
+ }
+ // FIXME: The left hand type and right hand type don't match
+ // FlowEntry.groupId() still returns a short value, not int.
+ return flowEntry.groupId().equals(groupId.get());
+ }
+ };
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/statistic/impl/package-info.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/statistic/impl/package-info.java
new file mode 100644
index 00000000..b1c9170a
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/statistic/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Core subsystem for responding to statistical inquiries.
+ */
+package org.onosproject.net.statistic.impl;
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/topology/impl/DefaultTopologyProvider.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/topology/impl/DefaultTopologyProvider.java
new file mode 100644
index 00000000..20a5ad36
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/topology/impl/DefaultTopologyProvider.java
@@ -0,0 +1,287 @@
+/*
+ * Copyright 2014-2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.topology.impl;
+
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static java.util.concurrent.Executors.newFixedThreadPool;
+import static org.onlab.util.Tools.get;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.core.CoreService.CORE_PROVIDER_ID;
+import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_ADDED;
+import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED;
+import static org.onosproject.net.device.DeviceEvent.Type.DEVICE_REMOVED;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.List;
+import java.util.Timer;
+import java.util.concurrent.ExecutorService;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Modified;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.util.AbstractAccumulator;
+import org.onlab.util.Accumulator;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.event.Event;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.link.LinkEvent;
+import org.onosproject.net.link.LinkListener;
+import org.onosproject.net.link.LinkService;
+import org.onosproject.net.provider.AbstractProvider;
+import org.onosproject.net.topology.DefaultGraphDescription;
+import org.onosproject.net.topology.GraphDescription;
+import org.onosproject.net.topology.TopologyProvider;
+import org.onosproject.net.topology.TopologyProviderRegistry;
+import org.onosproject.net.topology.TopologyProviderService;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Default implementation of a network topology provider that feeds off
+ * device and link subsystem events to trigger assembly and computation of
+ * new topology snapshots.
+ */
+@Component(immediate = true)
+@Service
+public class DefaultTopologyProvider extends AbstractProvider
+ implements TopologyProvider {
+
+ private static final int MAX_THREADS = 8;
+ private static final int DEFAULT_MAX_EVENTS = 1000;
+ private static final int DEFAULT_MAX_IDLE_MS = 10;
+ private static final int DEFAULT_MAX_BATCH_MS = 50;
+
+ // FIXME: Replace with a system-wide timer instance;
+ // TODO: Convert to use HashedWheelTimer or produce a variant of that; then decide which we want to adopt
+ private static final Timer TIMER = new Timer("onos-topo-event-batching");
+
+ @Property(name = "maxEvents", intValue = DEFAULT_MAX_EVENTS,
+ label = "Maximum number of events to accumulate")
+ private int maxEvents = DEFAULT_MAX_EVENTS;
+
+ @Property(name = "maxIdleMs", intValue = DEFAULT_MAX_IDLE_MS,
+ label = "Maximum number of millis between events")
+ private int maxIdleMs = DEFAULT_MAX_IDLE_MS;
+
+ @Property(name = "maxBatchMs", intValue = DEFAULT_MAX_BATCH_MS,
+ label = "Maximum number of millis for whole batch")
+ private int maxBatchMs = DEFAULT_MAX_BATCH_MS;
+
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected TopologyProviderRegistry providerRegistry;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LinkService linkService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ComponentConfigService cfgService;
+
+ private volatile boolean isStarted = false;
+
+ private TopologyProviderService providerService;
+ private final DeviceListener deviceListener = new InternalDeviceListener();
+ private final LinkListener linkListener = new InternalLinkListener();
+
+ private Accumulator<Event> accumulator;
+ private ExecutorService executor;
+
+ /**
+ * Creates a provider with the supplier identifier.
+ */
+ public DefaultTopologyProvider() {
+ super(CORE_PROVIDER_ID);
+ }
+
+ @Activate
+ public synchronized void activate(ComponentContext context) {
+ cfgService.registerProperties(DefaultTopologyProvider.class);
+ executor = newFixedThreadPool(MAX_THREADS, groupedThreads("onos/topo", "build-%d"));
+ accumulator = new TopologyChangeAccumulator();
+ logConfig("Configured");
+
+ modified(context);
+
+ providerService = providerRegistry.register(this);
+ deviceService.addListener(deviceListener);
+ linkService.addListener(linkListener);
+
+ isStarted = true;
+ triggerRecompute();
+ log.info("Started");
+ }
+
+ @Deactivate
+ public synchronized void deactivate(ComponentContext context) {
+ cfgService.unregisterProperties(DefaultTopologyProvider.class, false);
+ isStarted = false;
+
+ deviceService.removeListener(deviceListener);
+ linkService.removeListener(linkListener);
+ providerRegistry.unregister(this);
+ providerService = null;
+
+ executor.shutdownNow();
+ executor = null;
+
+ log.info("Stopped");
+ }
+
+ @Modified
+ public void modified(ComponentContext context) {
+ if (context == null) {
+ accumulator = new TopologyChangeAccumulator();
+ logConfig("Reconfigured");
+ return;
+ }
+
+ Dictionary<?, ?> properties = context.getProperties();
+ int newMaxEvents, newMaxBatchMs, newMaxIdleMs;
+ try {
+ String s = get(properties, "maxEvents");
+ newMaxEvents = isNullOrEmpty(s) ? maxEvents : Integer.parseInt(s.trim());
+
+ s = get(properties, "maxBatchMs");
+ newMaxBatchMs = isNullOrEmpty(s) ? maxBatchMs : Integer.parseInt(s.trim());
+
+ s = get(properties, "maxIdleMs");
+ newMaxIdleMs = isNullOrEmpty(s) ? maxIdleMs : Integer.parseInt(s.trim());
+
+ } catch (NumberFormatException | ClassCastException e) {
+ newMaxEvents = DEFAULT_MAX_EVENTS;
+ newMaxBatchMs = DEFAULT_MAX_BATCH_MS;
+ newMaxIdleMs = DEFAULT_MAX_IDLE_MS;
+ }
+
+ if (newMaxEvents != maxEvents || newMaxBatchMs != maxBatchMs || newMaxIdleMs != maxIdleMs) {
+ maxEvents = newMaxEvents;
+ maxBatchMs = newMaxBatchMs;
+ maxIdleMs = newMaxIdleMs;
+ accumulator = maxEvents > 1 ? new TopologyChangeAccumulator() : null;
+ logConfig("Reconfigured");
+ }
+ }
+
+ private void logConfig(String prefix) {
+ log.info("{} with maxEvents = {}; maxBatchMs = {}; maxIdleMs = {}; accumulator={}",
+ prefix, maxEvents, maxBatchMs, maxIdleMs, accumulator != null);
+ }
+
+
+ @Override
+ public void triggerRecompute() {
+ triggerTopologyBuild(Collections.<Event>emptyList());
+ }
+
+ /**
+ * Triggers assembly of topology data citing the specified events as the
+ * reason.
+ *
+ * @param reasons events which triggered the topology change
+ */
+ private synchronized void triggerTopologyBuild(List<Event> reasons) {
+ if (executor != null) {
+ executor.execute(new TopologyBuilderTask(reasons));
+ }
+ }
+
+ // Builds the topology using the latest device and link information
+ // and citing the specified events as reasons for the change.
+ private void buildTopology(List<Event> reasons) {
+ if (isStarted) {
+ GraphDescription desc =
+ new DefaultGraphDescription(System.nanoTime(),
+ System.currentTimeMillis(),
+ deviceService.getAvailableDevices(),
+ linkService.getActiveLinks());
+ providerService.topologyChanged(desc, reasons);
+ }
+ }
+
+ private void processEvent(Event event) {
+ if (accumulator != null) {
+ accumulator.add(event);
+ } else {
+ triggerTopologyBuild(ImmutableList.of(event));
+ }
+ }
+
+ // Callback for device events
+ private class InternalDeviceListener implements DeviceListener {
+ @Override
+ public void event(DeviceEvent event) {
+ DeviceEvent.Type type = event.type();
+ if (type == DEVICE_ADDED || type == DEVICE_REMOVED ||
+ type == DEVICE_AVAILABILITY_CHANGED) {
+ processEvent(event);
+ }
+ }
+ }
+
+ // Callback for link events
+ private class InternalLinkListener implements LinkListener {
+ @Override
+ public void event(LinkEvent event) {
+ processEvent(event);
+ }
+ }
+
+ // Event accumulator for paced triggering of topology assembly.
+ private class TopologyChangeAccumulator extends AbstractAccumulator<Event> {
+ TopologyChangeAccumulator() {
+ super(TIMER, maxEvents, maxBatchMs, maxIdleMs);
+ }
+
+ @Override
+ public void processItems(List<Event> items) {
+ triggerTopologyBuild(items);
+ }
+ }
+
+ // Task for building topology data in a separate thread.
+ private class TopologyBuilderTask implements Runnable {
+ private final List<Event> reasons;
+
+ public TopologyBuilderTask(List<Event> reasons) {
+ this.reasons = reasons;
+ }
+
+ @Override
+ public void run() {
+ try {
+ buildTopology(reasons);
+ } catch (Exception e) {
+ log.warn("Unable to compute topology", e);
+ }
+ }
+ }
+
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/topology/impl/PathManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/topology/impl/PathManager.java
new file mode 100644
index 00000000..a238c7fb
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/topology/impl/PathManager.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.topology.impl;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultEdgeLink;
+import org.onosproject.net.DefaultPath;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.EdgeLink;
+import org.onosproject.net.ElementId;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.HostLocation;
+import org.onosproject.net.Link;
+import org.onosproject.net.Path;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.net.topology.LinkWeight;
+import org.onosproject.net.topology.PathService;
+import org.onosproject.net.topology.Topology;
+import org.onosproject.net.topology.TopologyService;
+import org.slf4j.Logger;
+
+import java.util.List;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+import static org.onosproject.security.AppGuard.checkPermission;
+import static org.onosproject.security.AppPermission.Type.*;
+
+
+/**
+ * Provides implementation of a path selection service atop the current
+ * topology and host services.
+ */
+@Component(immediate = true)
+@Service
+public class PathManager implements PathService {
+
+ private static final String ELEMENT_ID_NULL = "Element ID cannot be null";
+
+ private static final ProviderId PID = new ProviderId("core", "org.onosproject.core");
+ private static final PortNumber P0 = PortNumber.portNumber(0);
+
+ private static final EdgeLink NOT_HOST = new NotHost();
+
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected TopologyService topologyService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostService hostService;
+
+ @Activate
+ public void activate() {
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ log.info("Stopped");
+ }
+
+ @Override
+ public Set<Path> getPaths(ElementId src, ElementId dst) {
+ checkPermission(TOPOLOGY_READ);
+
+ return getPaths(src, dst, null);
+ }
+
+ @Override
+ public Set<Path> getPaths(ElementId src, ElementId dst, LinkWeight weight) {
+ checkPermission(TOPOLOGY_READ);
+
+ checkNotNull(src, ELEMENT_ID_NULL);
+ checkNotNull(dst, ELEMENT_ID_NULL);
+
+ // Get the source and destination edge locations
+ EdgeLink srcEdge = getEdgeLink(src, true);
+ EdgeLink dstEdge = getEdgeLink(dst, false);
+
+ // If either edge is null, bail with no paths.
+ if (srcEdge == null || dstEdge == null) {
+ return ImmutableSet.of();
+ }
+
+ DeviceId srcDevice = srcEdge != NOT_HOST ? srcEdge.dst().deviceId() : (DeviceId) src;
+ DeviceId dstDevice = dstEdge != NOT_HOST ? dstEdge.src().deviceId() : (DeviceId) dst;
+
+ // If the source and destination are on the same edge device, there
+ // is just one path, so build it and return it.
+ if (srcDevice.equals(dstDevice)) {
+ return edgeToEdgePaths(srcEdge, dstEdge);
+ }
+
+ // Otherwise get all paths between the source and destination edge
+ // devices.
+ Topology topology = topologyService.currentTopology();
+ Set<Path> paths = weight == null ?
+ topologyService.getPaths(topology, srcDevice, dstDevice) :
+ topologyService.getPaths(topology, srcDevice, dstDevice, weight);
+
+ return edgeToEdgePaths(srcEdge, dstEdge, paths);
+ }
+
+ // Finds the host edge link if the element ID is a host id of an existing
+ // host. Otherwise, if the host does not exist, it returns null and if
+ // the element ID is not a host ID, returns NOT_HOST edge link.
+ private EdgeLink getEdgeLink(ElementId elementId, boolean isIngress) {
+ if (elementId instanceof HostId) {
+ // Resolve the host, return null.
+ Host host = hostService.getHost((HostId) elementId);
+ if (host == null) {
+ return null;
+ }
+ return new DefaultEdgeLink(PID, new ConnectPoint(elementId, P0),
+ host.location(), isIngress);
+ }
+ return NOT_HOST;
+ }
+
+ // Produces a set of edge-to-edge paths using the set of infrastructure
+ // paths and the given edge links.
+ private Set<Path> edgeToEdgePaths(EdgeLink srcLink, EdgeLink dstLink) {
+ Set<Path> endToEndPaths = Sets.newHashSetWithExpectedSize(1);
+ endToEndPaths.add(edgeToEdgePath(srcLink, dstLink, null));
+ return endToEndPaths;
+ }
+
+ // Produces a set of edge-to-edge paths using the set of infrastructure
+ // paths and the given edge links.
+ private Set<Path> edgeToEdgePaths(EdgeLink srcLink, EdgeLink dstLink, Set<Path> paths) {
+ Set<Path> endToEndPaths = Sets.newHashSetWithExpectedSize(paths.size());
+ for (Path path : paths) {
+ endToEndPaths.add(edgeToEdgePath(srcLink, dstLink, path));
+ }
+ return endToEndPaths;
+ }
+
+ // Produces a direct edge-to-edge path.
+ private Path edgeToEdgePath(EdgeLink srcLink, EdgeLink dstLink, Path path) {
+ List<Link> links = Lists.newArrayListWithCapacity(2);
+ // Add source and destination edge links only if they are real and
+ // add the infrastructure path only if it is not null.
+ if (srcLink != NOT_HOST) {
+ links.add(srcLink);
+ }
+ if (path != null) {
+ links.addAll(path.links());
+ }
+ if (dstLink != NOT_HOST) {
+ links.add(dstLink);
+ }
+ return new DefaultPath(PID, links, 2);
+ }
+
+ // Special value for edge link to represent that this is really not an
+ // edge link since the src or dst are really an infrastructure device.
+ private static class NotHost extends DefaultEdgeLink implements EdgeLink {
+ NotHost() {
+ super(PID, new ConnectPoint(HostId.NONE, P0),
+ new HostLocation(DeviceId.NONE, P0, 0L), false);
+ }
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/topology/impl/TopologyManager.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/topology/impl/TopologyManager.java
new file mode 100644
index 00000000..04c4f1c1
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/topology/impl/TopologyManager.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.net.topology.impl;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onosproject.net.provider.AbstractListenerProviderRegistry;
+import org.onosproject.event.Event;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link;
+import org.onosproject.net.Path;
+import org.onosproject.net.provider.AbstractProviderService;
+import org.onosproject.net.topology.ClusterId;
+import org.onosproject.net.topology.GraphDescription;
+import org.onosproject.net.topology.LinkWeight;
+import org.onosproject.net.topology.Topology;
+import org.onosproject.net.topology.TopologyCluster;
+import org.onosproject.net.topology.TopologyEvent;
+import org.onosproject.net.topology.TopologyGraph;
+import org.onosproject.net.topology.TopologyListener;
+import org.onosproject.net.topology.TopologyProvider;
+import org.onosproject.net.topology.TopologyProviderRegistry;
+import org.onosproject.net.topology.TopologyProviderService;
+import org.onosproject.net.topology.TopologyService;
+import org.onosproject.net.topology.TopologyStore;
+import org.onosproject.net.topology.TopologyStoreDelegate;
+import org.slf4j.Logger;
+
+import java.util.List;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.security.AppGuard.checkPermission;
+import static org.slf4j.LoggerFactory.getLogger;
+import static org.onosproject.security.AppPermission.Type.*;
+
+
+/**
+ * Provides basic implementation of the topology SB &amp; NB APIs.
+ */
+@Component(immediate = true)
+@Service
+public class TopologyManager
+ extends AbstractListenerProviderRegistry<TopologyEvent, TopologyListener,
+ TopologyProvider, TopologyProviderService>
+ implements TopologyService, TopologyProviderRegistry {
+
+ public static final String TOPOLOGY_NULL = "Topology cannot be null";
+ private static final String DEVICE_ID_NULL = "Device ID cannot be null";
+ private static final String CLUSTER_ID_NULL = "Cluster ID cannot be null";
+ private static final String CLUSTER_NULL = "Topology cluster cannot be null";
+ public static final String CONNECTION_POINT_NULL = "Connection point cannot be null";
+
+ private final Logger log = getLogger(getClass());
+
+ private TopologyStoreDelegate delegate = new InternalStoreDelegate();
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected TopologyStore store;
+
+ @Activate
+ public void activate() {
+ store.setDelegate(delegate);
+ eventDispatcher.addSink(TopologyEvent.class, listenerRegistry);
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ store.unsetDelegate(delegate);
+ eventDispatcher.removeSink(TopologyEvent.class);
+ log.info("Stopped");
+ }
+
+ @Override
+ public Topology currentTopology() {
+ checkPermission(TOPOLOGY_READ);
+ return store.currentTopology();
+ }
+
+ @Override
+ public boolean isLatest(Topology topology) {
+ checkPermission(TOPOLOGY_READ);
+ checkNotNull(topology, TOPOLOGY_NULL);
+ return store.isLatest(topology);
+ }
+
+ @Override
+ public Set<TopologyCluster> getClusters(Topology topology) {
+ checkPermission(TOPOLOGY_READ);
+ checkNotNull(topology, TOPOLOGY_NULL);
+ return store.getClusters(topology);
+ }
+
+ @Override
+ public TopologyCluster getCluster(Topology topology, ClusterId clusterId) {
+ checkPermission(TOPOLOGY_READ);
+ checkNotNull(topology, TOPOLOGY_NULL);
+ checkNotNull(topology, CLUSTER_ID_NULL);
+ return store.getCluster(topology, clusterId);
+ }
+
+ @Override
+ public Set<DeviceId> getClusterDevices(Topology topology, TopologyCluster cluster) {
+ checkPermission(TOPOLOGY_READ);
+ checkNotNull(topology, TOPOLOGY_NULL);
+ checkNotNull(topology, CLUSTER_NULL);
+ return store.getClusterDevices(topology, cluster);
+ }
+
+ @Override
+ public Set<Link> getClusterLinks(Topology topology, TopologyCluster cluster) {
+ checkPermission(TOPOLOGY_READ);
+ checkNotNull(topology, TOPOLOGY_NULL);
+ checkNotNull(topology, CLUSTER_NULL);
+ return store.getClusterLinks(topology, cluster);
+ }
+
+ @Override
+ public TopologyGraph getGraph(Topology topology) {
+ checkPermission(TOPOLOGY_READ);
+ checkNotNull(topology, TOPOLOGY_NULL);
+ return store.getGraph(topology);
+ }
+
+ @Override
+ public Set<Path> getPaths(Topology topology, DeviceId src, DeviceId dst) {
+ checkPermission(TOPOLOGY_READ);
+ checkNotNull(topology, TOPOLOGY_NULL);
+ checkNotNull(src, DEVICE_ID_NULL);
+ checkNotNull(dst, DEVICE_ID_NULL);
+ return store.getPaths(topology, src, dst);
+ }
+
+ @Override
+ public Set<Path> getPaths(Topology topology, DeviceId src, DeviceId dst, LinkWeight weight) {
+ checkPermission(TOPOLOGY_READ);
+
+ checkNotNull(topology, TOPOLOGY_NULL);
+ checkNotNull(src, DEVICE_ID_NULL);
+ checkNotNull(dst, DEVICE_ID_NULL);
+ checkNotNull(weight, "Link weight cannot be null");
+ return store.getPaths(topology, src, dst, weight);
+ }
+
+ @Override
+ public boolean isInfrastructure(Topology topology, ConnectPoint connectPoint) {
+ checkPermission(TOPOLOGY_READ);
+ checkNotNull(topology, TOPOLOGY_NULL);
+ checkNotNull(connectPoint, CONNECTION_POINT_NULL);
+ return store.isInfrastructure(topology, connectPoint);
+ }
+
+ @Override
+ public boolean isBroadcastPoint(Topology topology, ConnectPoint connectPoint) {
+ checkPermission(TOPOLOGY_READ);
+ checkNotNull(topology, TOPOLOGY_NULL);
+ checkNotNull(connectPoint, CONNECTION_POINT_NULL);
+ return store.isBroadcastPoint(topology, connectPoint);
+ }
+
+ // Personalized host provider service issued to the supplied provider.
+ @Override
+ protected TopologyProviderService createProviderService(TopologyProvider provider) {
+ return new InternalTopologyProviderService(provider);
+ }
+
+ private class InternalTopologyProviderService
+ extends AbstractProviderService<TopologyProvider>
+ implements TopologyProviderService {
+
+ InternalTopologyProviderService(TopologyProvider provider) {
+ super(provider);
+ }
+
+ @Override
+ public void topologyChanged(GraphDescription topoDescription,
+ List<Event> reasons) {
+ checkNotNull(topoDescription, "Topology description cannot be null");
+
+ TopologyEvent event = store.updateTopology(provider().id(),
+ topoDescription, reasons);
+ if (event != null) {
+ log.info("Topology {} changed", event.subject());
+ post(event);
+ }
+ }
+ }
+
+ // Store delegate to re-post events emitted from the store.
+ private class InternalStoreDelegate implements TopologyStoreDelegate {
+ @Override
+ public void notify(TopologyEvent event) {
+ post(event);
+ }
+ }
+}
diff --git a/framework/src/onos/core/net/src/main/java/org/onosproject/net/topology/impl/package-info.java b/framework/src/onos/core/net/src/main/java/org/onosproject/net/topology/impl/package-info.java
new file mode 100644
index 00000000..586bbf3b
--- /dev/null
+++ b/framework/src/onos/core/net/src/main/java/org/onosproject/net/topology/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Core subsystem for tracking global &amp; consistent topology graph views.
+ */
+package org.onosproject.net.topology.impl;