[ONOS-6248] VPLS refactoring
Change-Id: I8ffb2199ca108ad8dfe271681068636fc4af2a40
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/IntentInstaller.java b/apps/vpls/src/main/java/org/onosproject/vpls/IntentInstaller.java
deleted file mode 100644
index 94b819a..0000000
--- a/apps/vpls/src/main/java/org/onosproject/vpls/IntentInstaller.java
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
- * Copyright 2016-present 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.vpls;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
-import org.onlab.packet.MacAddress;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.EncapsulationType;
-import org.onosproject.net.FilteredConnectPoint;
-import org.onosproject.net.Host;
-import org.onosproject.net.flow.DefaultTrafficSelector;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.intent.ConnectivityIntent;
-import org.onosproject.net.intent.Constraint;
-import org.onosproject.net.intent.Intent;
-import org.onosproject.net.intent.IntentService;
-import org.onosproject.net.intent.IntentState;
-import org.onosproject.net.intent.Key;
-import org.onosproject.net.intent.MultiPointToSinglePointIntent;
-import org.onosproject.net.intent.SinglePointToMultiPointIntent;
-import org.onosproject.net.intent.constraint.EncapsulationConstraint;
-import org.onosproject.net.intent.constraint.PartialFailureConstraint;
-import org.onosproject.intentsync.IntentSynchronizationService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Set;
-
-import static org.onosproject.net.EncapsulationType.*;
-
-/**
- * Synchronizes intents between the in-memory intent store and the
- * IntentService.
- */
-public class IntentInstaller {
- private static final String SUBMIT =
- "Submitting intents to the Intent Synchronizer";
- private static final String WITHDRAW =
- "Withdrawing intents to the Intent Synchronizer";
- private static final String SP2MP =
- "Building sp2mp intent from {}";
- private static final String MP2SP =
- "Building mp2sp intent to {}";
-
- private static final Logger log = LoggerFactory.getLogger(
- IntentInstaller.class);
-
- private static final int PRIORITY_OFFSET = 1000;
-
- private static final Set<IntentState> WITHDRAWN_INTENT_STATES =
- ImmutableSet.of(IntentState.WITHDRAWN,
- IntentState.WITHDRAW_REQ,
- IntentState.WITHDRAWING);
-
- static final String PREFIX_BROADCAST = "brc";
- static final String PREFIX_UNICAST = "uni";
- static final String SEPARATOR = "-";
-
- private final ApplicationId appId;
- private final IntentSynchronizationService intentSynchronizer;
- private final IntentService intentService;
-
- public static final ImmutableList<Constraint> PARTIAL_FAILURE_CONSTRAINT =
- ImmutableList.of(new PartialFailureConstraint());
-
- /**
- * Class constructor.
- *
- * @param appId the Application ID
- * @param intentService the intent service
- * @param intentSynchronizer the intent synchronizer service
- */
- public IntentInstaller(ApplicationId appId, IntentService intentService,
- IntentSynchronizationService intentSynchronizer) {
- this.appId = appId;
- this.intentService = intentService;
- this.intentSynchronizer = intentSynchronizer;
- }
-
- /**
- * Requests to install the intents passed as argument to the Intent Service.
- *
- * @param intents intents to be submitted
- */
- protected void submitIntents(Collection<Intent> intents) {
- log.debug(SUBMIT);
- intents.forEach(intentSynchronizer::submit);
- }
-
- /**
- * Requests to withdraw the intents passed as argument to the Intent Service.
- *
- * @param intents intents to be withdraw
- */
- protected void withdrawIntents(Collection<Intent> intents) {
- log.debug(WITHDRAW);
- intents.forEach(intentSynchronizer::withdraw);
- }
-
- /**
- * Returns list of intents belongs to a VPLS.
- *
- * @param name the name of the VPLS
- * @return the list of intents belonging to a VPLS
- */
- protected List<Intent> getIntentsFromVpls(String name) {
- List<Intent> intents = Lists.newArrayList();
-
- intentService.getIntents().forEach(intent -> {
- if (intent.key().toString().startsWith(name)) {
- intents.add(intent);
- }
- });
-
- return intents;
- }
-
- /**
- * Builds a broadcast intent.
- *
- * @param key key to identify the intent
- * @param src the source connect point
- * @param dsts the destination connect points
- * @param encap the encapsulation type
- * @return the generated single-point to multi-point intent
- */
- protected SinglePointToMultiPointIntent buildBrcIntent(Key key,
- FilteredConnectPoint src,
- Set<FilteredConnectPoint> dsts,
- EncapsulationType encap) {
- log.debug("Building broadcast intent {} for source {}", SP2MP, src);
-
- SinglePointToMultiPointIntent.Builder intentBuilder;
-
- TrafficSelector selector = DefaultTrafficSelector.builder()
- .matchEthDst(MacAddress.BROADCAST)
- .build();
-
- intentBuilder = SinglePointToMultiPointIntent.builder()
- .appId(appId)
- .key(key)
- .selector(selector)
- .filteredIngressPoint(src)
- .filteredEgressPoints(dsts)
- .constraints(PARTIAL_FAILURE_CONSTRAINT)
- .priority(PRIORITY_OFFSET);
-
- setEncap(intentBuilder, PARTIAL_FAILURE_CONSTRAINT, encap);
-
- return intentBuilder.build();
- }
-
- /**
- * Builds a unicast intent.
- *
- * @param key key to identify the intent
- * @param srcs the source Connect Points
- * @param dst the destination Connect Point
- * @param host destination Host
- * @param encap the encapsulation type
- * @return the generated multi-point to single-point intent
- */
- protected MultiPointToSinglePointIntent buildUniIntent(Key key,
- Set<FilteredConnectPoint> srcs,
- FilteredConnectPoint dst,
- Host host,
- EncapsulationType encap) {
- log.debug("Building unicast intent {} for destination {}", MP2SP, dst);
-
- MultiPointToSinglePointIntent.Builder intentBuilder;
-
- TrafficSelector selector = DefaultTrafficSelector.builder()
- .matchEthDst(host.mac())
- .build();
-
- intentBuilder = MultiPointToSinglePointIntent.builder()
- .appId(appId)
- .key(key)
- .selector(selector)
- .filteredIngressPoints(srcs)
- .filteredEgressPoint(dst)
- .constraints(PARTIAL_FAILURE_CONSTRAINT)
- .priority(PRIORITY_OFFSET);
-
- setEncap(intentBuilder, PARTIAL_FAILURE_CONSTRAINT, encap);
-
- return intentBuilder.build();
- }
-
- /**
- * Builds an intent key either for single-point to multi-point or
- * multi-point to single-point intents, based on a prefix that defines
- * the type of intent, the single connect point representing the single
- * source or destination for that intent, the name of the VPLS the intent
- * belongs to, and the destination host MAC address the intent reaches.
- *
- * @param prefix the key prefix
- * @param cPoint the connect point identifying the source/destination
- * @param vplsName the name of the VPLS
- * @param hostMac the source/destination MAC address
- * @return the key to identify the intent
- */
- protected Key buildKey(String prefix,
- ConnectPoint cPoint,
- String vplsName,
- MacAddress hostMac) {
- String keyString = vplsName +
- SEPARATOR +
- prefix +
- SEPARATOR +
- cPoint.deviceId() +
- SEPARATOR +
- cPoint.port() +
- SEPARATOR +
- hostMac;
-
- return Key.of(keyString, appId);
- }
-
- /**
- * Returns true if the specified intent exists; false otherwise.
- *
- * @param intentKey the intent key
- * @return true if the intent exists; false otherwise
- */
- protected boolean intentExists(Key intentKey) {
- if (intentService.getIntent(intentKey) == null) {
- return false;
- }
-
- // Intent does not exist if intent withdrawn
- IntentState currentIntentState = intentService.getIntentState(intentKey);
- return !WITHDRAWN_INTENT_STATES.contains(currentIntentState);
-
- }
-
- /**
- * Sets one or more encapsulation constraints on the intent builder given.
- *
- * @param builder the intent builder
- * @param constraints the existing intent constraints
- * @param encap the encapsulation type to be set
- */
- public static void setEncap(ConnectivityIntent.Builder builder,
- List<Constraint> constraints,
- EncapsulationType encap) {
- // Constraints might be an immutable list, so a new modifiable list
- // is created
- List<Constraint> newConstraints = new ArrayList<>(constraints);
-
- // Remove any encapsulation constraint if already in the list
- constraints.stream()
- .filter(c -> c instanceof EncapsulationConstraint)
- .forEach(newConstraints::remove);
-
- // if the new encapsulation is different from NONE, a new encapsulation
- // constraint should be added to the list
- if (!encap.equals(NONE)) {
- newConstraints.add(new EncapsulationConstraint(encap));
- }
-
- // Submit new constraint list as immutable list
- builder.constraints(ImmutableList.copyOf(newConstraints));
- }
-}
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/Vpls.java b/apps/vpls/src/main/java/org/onosproject/vpls/Vpls.java
deleted file mode 100644
index af47248..0000000
--- a/apps/vpls/src/main/java/org/onosproject/vpls/Vpls.java
+++ /dev/null
@@ -1,432 +0,0 @@
-/*
- * Copyright 2015-present 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.vpls;
-
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
-import com.google.common.collect.SetMultimap;
-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.MacAddress;
-import org.onlab.packet.VlanId;
-import org.onosproject.app.ApplicationService;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.core.CoreService;
-import org.onosproject.incubator.net.intf.Interface;
-import org.onosproject.incubator.net.intf.InterfaceEvent;
-import org.onosproject.incubator.net.intf.InterfaceListener;
-import org.onosproject.incubator.net.intf.InterfaceService;
-import org.onosproject.net.EncapsulationType;
-import org.onosproject.net.FilteredConnectPoint;
-import org.onosproject.net.Host;
-import org.onosproject.net.config.NetworkConfigEvent;
-import org.onosproject.net.config.NetworkConfigListener;
-import org.onosproject.net.config.NetworkConfigService;
-import org.onosproject.net.flow.DefaultTrafficSelector;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flow.criteria.Criterion;
-import org.onosproject.net.flow.criteria.VlanIdCriterion;
-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.IntentService;
-import org.onosproject.net.intent.Key;
-import org.onosproject.intentsync.IntentSynchronizationService;
-import org.onosproject.vpls.config.VplsConfigService;
-import org.slf4j.Logger;
-
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import static org.onosproject.vpls.IntentInstaller.PREFIX_BROADCAST;
-import static org.onosproject.vpls.IntentInstaller.PREFIX_UNICAST;
-import static org.slf4j.LoggerFactory.getLogger;
-
-/**
- * Application to create L2 broadcast overlay networks using VLANs.
- */
-@Component(immediate = true)
-public class Vpls {
- static final String VPLS_APP = "org.onosproject.vpls";
-
- private static final String HOST_FCP_NOT_FOUND =
- "Filtered connected point for host {} not found";
- private static final String HOST_EVENT = "Received HostEvent {}";
- private static final String INTF_CONF_EVENT =
- "Received InterfaceConfigEvent {}";
- private static final String NET_CONF_EVENT =
- "Received NetworkConfigEvent {}";
-
- private final Logger log = getLogger(getClass());
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected ApplicationService applicationService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected CoreService coreService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected HostService hostService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected IntentService intentService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected InterfaceService interfaceService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected IntentSynchronizationService intentSynchronizer;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected NetworkConfigService configService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected VplsConfigService vplsConfigService;
-
- private final HostListener hostListener = new InternalHostListener();
-
- private final InternalInterfaceListener interfaceListener =
- new InternalInterfaceListener();
-
- private final InternalNetworkConfigListener configListener =
- new InternalNetworkConfigListener();
-
- private IntentInstaller intentInstaller;
-
- private ApplicationId appId;
-
- @Activate
- public void activate() {
- appId = coreService.registerApplication(VPLS_APP);
-
- intentInstaller = new IntentInstaller(appId,
- intentService,
- intentSynchronizer);
-
- applicationService.registerDeactivateHook(appId, () -> {
- intentSynchronizer.removeIntentsByAppId(appId);
- });
-
- hostService.addListener(hostListener);
- interfaceService.addListener(interfaceListener);
- configService.addListener(configListener);
-
- setupConnectivity(false);
-
- log.info("Activated");
- }
-
- @Deactivate
- public void deactivate() {
- hostService.removeListener(hostListener);
- interfaceService.removeListener(interfaceListener);
- configService.removeListener(configListener);
- intentSynchronizer.removeIntentsByAppId(appId);
- log.info("Deactivated");
- }
-
- /**
- * Sets up connectivity for all VPLSs.
- *
- * @param isNetworkConfigEvent true if this function is triggered
- * by NetworkConfigEvent; false otherwise
- */
- private void setupConnectivity(boolean isNetworkConfigEvent) {
- SetMultimap<String, Interface> networkInterfaces =
- vplsConfigService.ifacesByVplsName();
-
- Set<String> vplsAffectedByApi =
- new HashSet<>(vplsConfigService.vplsAffectedByApi());
-
- if (isNetworkConfigEvent && vplsAffectedByApi.isEmpty()) {
- vplsAffectedByApi.addAll(vplsConfigService.vplsNamesOld());
- }
-
- networkInterfaces.asMap().forEach((vplsName, interfaces) -> {
- Set<Host> hosts = Sets.newHashSet();
- interfaces.forEach(intf -> {
- // Add hosts that belongs to the specific VPLS
- hostService.getConnectedHosts(intf.connectPoint())
- .stream()
- .filter(host -> host.vlan().equals(intf.vlan()))
- .forEach(hosts::add);
- });
-
- EncapsulationType encap =
- vplsConfigService.encap(vplsName);
-
- setupConnectivity(vplsName, interfaces, hosts, encap,
- vplsAffectedByApi.contains(vplsName));
- vplsAffectedByApi.remove(vplsName);
- });
-
- if (!vplsAffectedByApi.isEmpty()) {
- for (String vplsName : vplsAffectedByApi) {
- withdrawIntents(vplsName, Lists.newArrayList());
- }
- }
- }
-
- /**
- * Sets up connectivity for specific VPLS.
- *
- * @param vplsName the VPLS name
- * @param interfaces the interfaces that belong to the VPLS
- * @param hosts the hosts that belong to the VPLS
- * @param encap the encapsulation type
- * @param affectedByApi true if this function is triggered from the APIs;
- * false otherwise
- */
- private void setupConnectivity(String vplsName,
- Collection<Interface> interfaces,
- Set<Host> hosts,
- EncapsulationType encap,
- boolean affectedByApi) {
-
- List<Intent> intents = Lists.newArrayList();
- List<Key> keys = Lists.newArrayList();
- Set<FilteredConnectPoint> fcPoints = buildFCPoints(interfaces);
-
- intents.addAll(buildBroadcastIntents(
- vplsName, fcPoints, encap, affectedByApi));
- intents.addAll(buildUnicastIntents(
- vplsName, hosts, fcPoints, encap, affectedByApi));
-
- if (affectedByApi) {
- intents.forEach(intent -> keys.add(intent.key()));
- withdrawIntents(vplsName, keys);
- }
-
- intentInstaller.submitIntents(intents);
- }
-
- /**
- * Withdraws intents belonging to a VPLS, given a VPLS name.
- *
- * @param vplsName the VPLS name
- * @param keys the keys of the intents to be installed
- */
- private void withdrawIntents(String vplsName, List<Key> keys) {
- List<Intent> intents = Lists.newArrayList();
-
- intentInstaller.getIntentsFromVpls(vplsName)
- .forEach(intent -> {
- if (!keys.contains(intent.key())) {
- intents.add(intent);
- }
- });
-
- intentInstaller.withdrawIntents(intents);
- }
-
- /**
- * Sets up broadcast intents between any given filtered connect point.
- *
- * @param vplsName the VPLS name
- * @param fcPoints the set of filtered connect points
- * @param encap the encapsulation type
- * @param affectedByApi true if the function triggered from APIs;
- * false otherwise
- * @return the set of broadcast intents
- */
- private Set<Intent> buildBroadcastIntents(String vplsName,
- Set<FilteredConnectPoint> fcPoints,
- EncapsulationType encap,
- boolean affectedByApi) {
- Set<Intent> intents = Sets.newHashSet();
- fcPoints.forEach(point -> {
- Set<FilteredConnectPoint> otherPoints =
- fcPoints.stream()
- .filter(fcp -> !fcp.equals(point))
- .collect(Collectors.toSet());
-
- Key brcKey = intentInstaller.buildKey(PREFIX_BROADCAST,
- point.connectPoint(),
- vplsName,
- MacAddress.BROADCAST);
-
- if ((!intentInstaller.intentExists(brcKey) || affectedByApi)
- && !otherPoints.isEmpty()) {
- intents.add(intentInstaller.buildBrcIntent(brcKey,
- point,
- otherPoints,
- encap));
- }
- });
-
- return ImmutableSet.copyOf(intents);
- }
-
- /**
- * Sets up unicast intents between any given filtered connect point.
- *
- * @param vplsName the VPLS name
- * @param hosts the set of destination hosts
- * @param fcPoints the set of filtered connect points
- * @param encap the encapsulation type
- * @param affectedByApi true if the function triggered from APIs;
- * false otherwise
- * @return the set of unicast intents
- */
- private Set<Intent> buildUnicastIntents(String vplsName,
- Set<Host> hosts,
- Set<FilteredConnectPoint> fcPoints,
- EncapsulationType encap,
- boolean affectedByApi) {
- Set<Intent> intents = Sets.newHashSet();
- hosts.forEach(host -> {
- FilteredConnectPoint hostPoint = getHostPoint(host, fcPoints);
-
- if (hostPoint == null) {
- log.warn(HOST_FCP_NOT_FOUND, host);
- return;
- }
-
- Set<FilteredConnectPoint> otherPoints =
- fcPoints.stream()
- .filter(fcp -> !fcp.equals(hostPoint))
- .collect(Collectors.toSet());
-
- Key uniKey = intentInstaller.buildKey(PREFIX_UNICAST,
- host.location(),
- vplsName,
- host.mac());
-
- if ((!intentInstaller.intentExists(uniKey) || affectedByApi) &&
- !otherPoints.isEmpty()) {
- intents.add(intentInstaller.buildUniIntent(uniKey,
- otherPoints,
- hostPoint,
- host,
- encap));
- }
- });
-
- return ImmutableSet.copyOf(intents);
- }
-
- /**
- * Returns the filtered connect point associated to a given host.
- *
- * @param host the target host
- * @param fcps the filtered connected points
- * @return null if not found; the filtered connect point otherwise
- */
- private FilteredConnectPoint getHostPoint(Host host,
- Set<FilteredConnectPoint> fcps) {
- return fcps.stream()
- .filter(fcp -> fcp.connectPoint().equals(host.location()))
- .filter(fcp -> {
- VlanIdCriterion vlanCriterion =
- (VlanIdCriterion) fcp.trafficSelector().
- getCriterion(Criterion.Type.VLAN_VID);
- return vlanCriterion == null ||
- vlanCriterion.vlanId().equals(host.vlan());
- })
- .findFirst()
- .orElse(null);
- }
-
- /**
- * Computes a set of filtered connect points from a list of given interfaces.
- *
- * @param interfaces the interfaces to compute
- * @return the set of filtered connect points
- */
- private Set<FilteredConnectPoint> buildFCPoints(Collection<Interface> interfaces) {
- // Build all filtered connected points in the VPLS
- return interfaces
- .stream()
- .map(intf -> {
- TrafficSelector.Builder selectorBuilder =
- DefaultTrafficSelector.builder();
- if (!intf.vlan().equals(VlanId.NONE)) {
- selectorBuilder.matchVlanId(intf.vlan());
- }
- return new FilteredConnectPoint(intf.connectPoint(),
- selectorBuilder.build());
- })
- .collect(Collectors.toSet());
- }
-
- /**
- * Listener for host events.
- */
- private class InternalHostListener implements HostListener {
- @Override
- public void event(HostEvent event) {
- log.debug(HOST_EVENT, event);
- switch (event.type()) {
- case HOST_ADDED:
- case HOST_UPDATED:
- case HOST_REMOVED:
- setupConnectivity(false);
- break;
-
- default:
- break;
- }
- }
- }
-
- /**
- * Listener for interface configuration events.
- */
- private class InternalInterfaceListener implements InterfaceListener {
- @Override
- public void event(InterfaceEvent event) {
- log.debug(INTF_CONF_EVENT, event);
- switch (event.type()) {
- case INTERFACE_ADDED:
- case INTERFACE_UPDATED:
- case INTERFACE_REMOVED:
- setupConnectivity(false);
- break;
- default:
- break;
- }
- }
- }
-
- /**
- * Listener for VPLS configuration events.
- */
- private class InternalNetworkConfigListener implements NetworkConfigListener {
- @Override
- public void event(NetworkConfigEvent event) {
- if (event.configClass() == VplsConfigService.CONFIG_CLASS) {
- log.debug(NET_CONF_EVENT, event.configClass());
- switch (event.type()) {
- case CONFIG_ADDED:
- case CONFIG_UPDATED:
- case CONFIG_REMOVED:
- setupConnectivity(true);
- break;
- default:
- break;
- }
- }
- }
- }
-}
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/VplsManager.java b/apps/vpls/src/main/java/org/onosproject/vpls/VplsManager.java
new file mode 100644
index 0000000..58ea4a2
--- /dev/null
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/VplsManager.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright 2015-present 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.vpls;
+
+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.incubator.net.intf.Interface;
+import org.onosproject.incubator.net.intf.InterfaceService;
+import org.onosproject.net.EncapsulationType;
+import org.onosproject.net.Host;
+import org.onosproject.net.host.HostEvent;
+import org.onosproject.net.host.HostListener;
+import org.onosproject.net.host.HostService;
+import org.onosproject.store.StoreDelegate;
+import org.onosproject.vpls.api.VplsData;
+import org.onosproject.vpls.api.VplsOperationService;
+import org.onosproject.vpls.api.VplsOperation;
+import org.onosproject.vpls.api.Vpls;
+import org.onosproject.vpls.api.VplsStore;
+import org.onosproject.vpls.store.VplsStoreEvent;
+import org.slf4j.Logger;
+
+import java.util.Collection;
+import java.util.Set;
+
+import static java.util.Objects.*;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Application to create L2 broadcast overlay networks using VLANs.
+ */
+@Component(immediate = true)
+@Service
+public class VplsManager implements Vpls {
+ public static final String VPLS_APP = "org.onosproject.vpls";
+ private static final String UNSUPPORTED_STORE_EVENT_TYPE =
+ "Unsupported store event type {}.";
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostService hostService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected InterfaceService interfaceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected VplsStore vplsStore;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected VplsOperationService operationService;
+
+ private StoreDelegate<VplsStoreEvent> vplsStoreDelegate;
+ private HostListener vplsHostListener;
+
+ @Activate
+ public void activate() {
+ vplsStoreDelegate = new VplsStoreDelegate();
+ vplsHostListener = new VplsHostListener();
+
+ vplsStore.setDelegate(vplsStoreDelegate);
+ hostService.addListener(vplsHostListener);
+ }
+
+ @Deactivate
+ public void deactivate() {
+ vplsStore.unsetDelegate(vplsStoreDelegate);
+ hostService.removeListener(vplsHostListener);
+ }
+
+ @Override
+ public VplsData createVpls(String vplsName, EncapsulationType encapsulationType) {
+ requireNonNull(vplsName);
+ requireNonNull(encapsulationType);
+
+ if (vplsStore.getVpls(vplsName) != null) {
+ return null;
+ }
+
+ VplsData vplsData = VplsData.of(vplsName, encapsulationType);
+ vplsStore.addVpls(vplsData);
+
+ return vplsData;
+ }
+
+ @Override
+ public VplsData removeVpls(VplsData vplsData) {
+ requireNonNull(vplsData);
+ vplsData.state(VplsData.VplsState.REMOVING);
+ vplsStore.removeVpls(vplsData);
+ return vplsData;
+ }
+
+ @Override
+ public void addInterfaces(VplsData vplsData, Collection<Interface> interfaces) {
+ requireNonNull(vplsData);
+ requireNonNull(interfaces);
+ vplsData.addInterfaces(interfaces);
+ updateVplsStatus(vplsData, VplsData.VplsState.UPDATING);
+ }
+
+ @Override
+ public void addInterface(VplsData vplsData, Interface iface) {
+ requireNonNull(vplsData);
+ requireNonNull(iface);
+ vplsData.addInterface(iface);
+ updateVplsStatus(vplsData, VplsData.VplsState.UPDATING);
+ }
+
+ @Override
+ public void setEncapsulationType(VplsData vplsData, EncapsulationType encapsulationType) {
+ requireNonNull(vplsData);
+ requireNonNull(encapsulationType);
+ if (vplsData.encapsulationType().equals(encapsulationType)) {
+ // Encap type not changed.
+ return;
+ }
+ vplsData.encapsulationType(encapsulationType);
+ updateVplsStatus(vplsData, VplsData.VplsState.UPDATING);
+ }
+
+ @Override
+ public VplsData getVpls(String vplsName) {
+ requireNonNull(vplsName);
+ return vplsStore.getVpls(vplsName);
+ }
+
+ @Override
+ public Collection<Interface> removeInterfaces(VplsData vplsData, Collection<Interface> interfaces) {
+ requireNonNull(vplsData);
+ requireNonNull(interfaces);
+ vplsData.removeInterfaces(interfaces);
+ updateVplsStatus(vplsData, VplsData.VplsState.UPDATING);
+ return interfaces;
+ }
+
+ @Override
+ public Interface removeInterface(VplsData vplsData, Interface iface) {
+ requireNonNull(vplsData);
+ requireNonNull(iface);
+ vplsData.removeInterface(iface);
+ updateVplsStatus(vplsData, VplsData.VplsState.UPDATING);
+ return iface;
+ }
+
+ @Override
+ public void removeAllVpls() {
+ Set<VplsData> allVplses = ImmutableSet.copyOf(vplsStore.getAllVpls());
+ allVplses.forEach(this::removeVpls);
+ }
+
+ @Override
+ public Collection<VplsData> getAllVpls() {
+ return ImmutableSet.copyOf(vplsStore.getAllVpls());
+ }
+
+ /**
+ * Updates VPLS status to the store.
+ *
+ * @param vplsData the VPLS
+ * @param vplsState the new state to the VPLS
+ */
+ private void updateVplsStatus(VplsData vplsData, VplsData.VplsState vplsState) {
+ vplsData.state(vplsState);
+ vplsStore.updateVpls(vplsData);
+ }
+
+ /**
+ * A listener for host events.
+ * Updates a VPLS if host added or removed.
+ */
+ class VplsHostListener implements HostListener {
+ @Override
+ public void event(HostEvent event) {
+ Host host = event.subject();
+ Interface iface = getHostInterface(host);
+ if (iface == null) {
+ return;
+ }
+ VplsData vplsData = vplsStore.getAllVpls().stream()
+ .filter(v -> v.interfaces().contains(iface))
+ .findFirst()
+ .orElse(null);
+ if (vplsData == null) {
+ // the host does not related to any vpls
+ return;
+ }
+ updateVplsStatus(vplsData, VplsData.VplsState.UPDATING);
+ }
+
+ /**
+ * Gets the network interface of the host.
+ *
+ * @param host the host
+ * @return the network interface of the host; null if no network
+ * interface found
+ */
+ private Interface getHostInterface(Host host) {
+ Set<Interface> interfaces = interfaceService.getInterfaces();
+ return interfaces.stream()
+ .filter(iface -> iface.connectPoint().equals(host.location()) &&
+ iface.vlan().equals(host.vlan()))
+ .findFirst()
+ .orElse(null);
+ }
+ }
+
+ /**
+ * Store delegate for VPLS store.
+ * Handles VPLS store event and generate VPLS operation according to event
+ * type.
+ */
+ class VplsStoreDelegate implements StoreDelegate<VplsStoreEvent> {
+
+ @Override
+ public void notify(VplsStoreEvent event) {
+ VplsOperation vplsOperation;
+ VplsOperation.Operation op;
+ VplsData vplsData = event.subject();
+ switch (event.type()) {
+ case ADD:
+ op = VplsOperation.Operation.ADD;
+ break;
+ case REMOVE:
+ op = VplsOperation.Operation.REMOVE;
+ break;
+ case UPDATE:
+ if (vplsData.state() == VplsData.VplsState.FAILED ||
+ vplsData.state() == VplsData.VplsState.ADDED ||
+ vplsData.state() == VplsData.VplsState.REMOVED) {
+ // Update the state only. Nothing to do if it is updated
+ // to ADDED, REMOVED or FAILED
+ op = null;
+ } else {
+ op = VplsOperation.Operation.UPDATE;
+ }
+ break;
+ default:
+ log.warn(UNSUPPORTED_STORE_EVENT_TYPE, event.type());
+ return;
+ }
+ if (op != null) {
+ vplsOperation = VplsOperation.of(vplsData, op);
+ operationService.submit(vplsOperation);
+ }
+ }
+ }
+}
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/VplsNeighbourHandler.java b/apps/vpls/src/main/java/org/onosproject/vpls/VplsNeighbourHandler.java
index de4365f..8a3ac7d 100644
--- a/apps/vpls/src/main/java/org/onosproject/vpls/VplsNeighbourHandler.java
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/VplsNeighbourHandler.java
@@ -15,12 +15,13 @@
*/
package org.onosproject.vpls;
-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.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.incubator.net.intf.Interface;
@@ -30,16 +31,20 @@
import org.onosproject.incubator.net.neighbour.NeighbourMessageContext;
import org.onosproject.incubator.net.neighbour.NeighbourMessageHandler;
import org.onosproject.incubator.net.neighbour.NeighbourResolutionService;
+import org.onosproject.net.ConnectPoint;
import org.onosproject.net.Host;
import org.onosproject.net.config.NetworkConfigEvent;
import org.onosproject.net.config.NetworkConfigListener;
import org.onosproject.net.config.NetworkConfigService;
-import org.onosproject.net.device.DeviceService;
import org.onosproject.net.host.HostService;
-import org.onosproject.vpls.config.VplsConfigService;
+import org.onosproject.vpls.api.VplsData;
+import org.onosproject.vpls.api.VplsStore;
import org.slf4j.Logger;
+import java.util.Collection;
+import java.util.Objects;
import java.util.Set;
+import java.util.stream.Collectors;
import static org.slf4j.LoggerFactory.getLogger;
@@ -58,16 +63,13 @@
protected CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected DeviceService deviceService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected InterfaceService interfaceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected NeighbourResolutionService neighbourService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected VplsConfigService vplsConfigService;
+ protected VplsStore vplsStore;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected NetworkConfigService configService;
@@ -88,7 +90,7 @@
@Activate
protected void activate() {
- appId = coreService.registerApplication(Vpls.VPLS_APP);
+ appId = coreService.registerApplication(VplsManager.VPLS_APP);
interfaceService.addListener(interfaceListener);
configService.addListener(configListener);
configNeighbourHandler();
@@ -101,18 +103,16 @@
neighbourService.unregisterNeighbourHandlers(appId);
}
- private void configNeighbourHandler() {
+ /**
+ * Registers neighbour handler to all available interfaces.
+ */
+ protected void configNeighbourHandler() {
neighbourService.unregisterNeighbourHandlers(appId);
- Set<Interface> interfaces = vplsConfigService.allIfaces();
-
- interfaceService.getInterfaces()
- .stream()
- .filter(interfaces::contains)
- .forEach(intf -> {
- neighbourService.registerNeighbourHandler(intf,
- neighbourHandler,
- appId);
- });
+ interfaceService
+ .getInterfaces()
+ .forEach(intf -> neighbourService.registerNeighbourHandler(intf,
+ neighbourHandler,
+ appId));
}
/**
@@ -123,16 +123,13 @@
@Override
public void handleMessage(NeighbourMessageContext context,
HostService hostService) {
-
switch (context.type()) {
case REQUEST:
handleRequest(context);
break;
-
case REPLY:
handleReply(context, hostService);
break;
-
default:
log.warn(UNKNOWN_CONTEXT, context.type());
break;
@@ -146,16 +143,15 @@
* @param context the message context
*/
protected void handleRequest(NeighbourMessageContext context) {
- SetMultimap<String, Interface> interfaces =
- vplsConfigService.ifacesByVplsName(context.vlan(),
- context.inPort());
- if (interfaces != null) {
- interfaces.values().stream()
+ // Find target VPLS first, then broadcast to all interface of this VPLS
+ VplsData vplsData = findVpls(context);
+ if (vplsData != null) {
+ vplsData.interfaces().stream()
.filter(intf -> !context.inPort().equals(intf.connectPoint()))
.forEach(context::forward);
-
} else {
- log.debug(CAN_NOT_FIND_VPLS, context.inPort(), context.vlan());
+ log.warn(CAN_NOT_FIND_VPLS, context.inPort(), context.vlan());
+ context.drop();
}
}
@@ -167,21 +163,66 @@
*/
protected void handleReply(NeighbourMessageContext context,
HostService hostService) {
- Set<Host> hosts = hostService.getHostsByMac(context.dstMac());
- SetMultimap<String, Interface> interfaces =
- vplsConfigService.ifacesByVplsName(context.vlan(),
- context.inPort());
- if (interfaces != null) {
- hosts.forEach(host -> interfaces.values().stream()
- .filter(intf -> intf.connectPoint().equals(host.location()))
- .filter(intf -> intf.vlan().equals(host.vlan()))
- .forEach(context::forward));
+ // Find target VPLS, then reply to the host
+ VplsData vplsData = findVpls(context);
+ if (vplsData != null) {
+ MacAddress dstMac = context.dstMac();
+ Set<Host> hosts = hostService.getHostsByMac(dstMac);
+ hosts = hosts.stream()
+ .filter(host -> vplsData.interfaces().contains(getHostInterface(host)))
+ .collect(Collectors.toSet());
+
+ // reply to all host in same VPLS
+ hosts.stream()
+ .map(this::getHostInterface)
+ .filter(Objects::nonNull)
+ .forEach(context::forward);
} else {
- log.debug(CAN_NOT_FIND_VPLS, context.inPort(), context.vlan());
+ // this might be happened when we remove an interface from VPLS
+ // just ignore this message
+ log.warn(CAN_NOT_FIND_VPLS, context.inPort(), context.vlan());
+ context.drop();
}
}
/**
+ * Finds the VPLS with given neighbour message context.
+ *
+ * @param context the neighbour message context
+ * @return the VPLS for specific neighbour message context
+ */
+ private VplsData findVpls(NeighbourMessageContext context) {
+ Collection<VplsData> vplses = vplsStore.getAllVpls();
+ for (VplsData vplsData : vplses) {
+ Set<Interface> interfaces = vplsData.interfaces();
+ ConnectPoint port = context.inPort();
+ VlanId vlanId = context.vlan();
+ boolean match = interfaces.stream()
+ .anyMatch(iface -> iface.connectPoint().equals(port) &&
+ iface.vlan().equals(vlanId));
+ if (match) {
+ return vplsData;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Finds the network interface related to the host.
+ *
+ * @param host the host
+ * @return the interface related to the host
+ */
+ private Interface getHostInterface(Host host) {
+ Set<Interface> interfaces = interfaceService.getInterfaces();
+ return interfaces.stream()
+ .filter(iface -> iface.connectPoint().equals(host.location()) &&
+ iface.vlan().equals(host.vlan()))
+ .findFirst()
+ .orElse(null);
+ }
+
+ /**
* Listener for interface configuration events.
*/
private class VplsInterfaceListener implements InterfaceListener {
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/VplsOperationManager.java b/apps/vpls/src/main/java/org/onosproject/vpls/VplsOperationManager.java
new file mode 100644
index 0000000..4c0262d
--- /dev/null
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/VplsOperationManager.java
@@ -0,0 +1,645 @@
+/*
+ * Copyright 2017-present 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.vpls;
+
+import com.google.common.collect.Maps;
+import com.google.common.collect.Queues;
+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.util.Tools;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.LeadershipEvent;
+import org.onosproject.cluster.LeadershipEventListener;
+import org.onosproject.cluster.LeadershipService;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.net.Host;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentEvent;
+import org.onosproject.net.intent.IntentException;
+import org.onosproject.net.intent.IntentListener;
+import org.onosproject.net.intent.IntentService;
+import org.onosproject.net.intent.IntentUtils;
+import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.MultiPointToSinglePointIntent;
+import org.onosproject.net.intent.SinglePointToMultiPointIntent;
+import org.onosproject.vpls.api.VplsData;
+import org.onosproject.vpls.api.VplsOperationException;
+import org.onosproject.vpls.api.VplsOperationService;
+import org.onosproject.vpls.api.VplsOperation;
+import org.onosproject.vpls.api.VplsStore;
+import org.onosproject.vpls.intent.VplsIntentUtility;
+import org.slf4j.Logger;
+
+import java.util.Collection;
+import java.util.Deque;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static org.onlab.util.BoundedThreadPool.newFixedThreadPool;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * An implementation of VplsOperationService.
+ * Handles the execution order of the VPLS operations generated by the
+ * application.
+ */
+@Component(immediate = true)
+@Service
+public class VplsOperationManager implements VplsOperationService {
+ private static final int NUM_THREADS = 4;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentService intentService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LeadershipService leadershipService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ClusterService clusterService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostService hostService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected VplsStore vplsStore;
+
+ private final Logger log = getLogger(getClass());
+ protected Map<String, Deque<VplsOperation>> pendingVplsOperations;
+ protected final Map<String, VplsOperation> runningOperations = Maps.newHashMap();
+ protected ScheduledExecutorService schedulerExecutor;
+ protected ExecutorService workerExecutor;
+ protected ApplicationId appId;
+ protected boolean isLeader;
+ protected NodeId localNodeId;
+ protected LeadershipEventListener leadershipEventListener;
+
+ @Activate
+ public void activate() {
+ appId = coreService.registerApplication(VplsManager.VPLS_APP);
+ localNodeId = clusterService.getLocalNode().id();
+
+ leadershipEventListener = new InternalLeadershipListener();
+ leadershipService.addListener(leadershipEventListener);
+ leadershipService.runForLeadership(appId.name());
+ pendingVplsOperations = Maps.newConcurrentMap();
+
+ // Thread pool for VplsOperationExecutor
+ workerExecutor = newFixedThreadPool(NUM_THREADS,
+ groupedThreads("onos/apps/vpls",
+ "worker-%d",
+ log));
+ // A single thread pool for VplsOperationScheduler
+ schedulerExecutor = Executors.newScheduledThreadPool(1,
+ groupedThreads("onos/apps/vpls",
+ "scheduler-%d",
+ log));
+ // Start the scheduler
+ schedulerExecutor.scheduleAtFixedRate(new VplsOperationScheduler(),
+ 0,
+ 500,
+ TimeUnit.MILLISECONDS);
+
+ }
+
+ @Deactivate
+ public void deactivate() {
+ pendingVplsOperations.clear();
+ runningOperations.clear();
+ leadershipService.removeListener(leadershipEventListener);
+ schedulerExecutor.shutdown();
+ workerExecutor.shutdown();
+
+ // remove all intents from VPLS application when deactivated
+ Tools.stream(intentService.getIntents())
+ .filter(intent -> intent.appId().equals(appId))
+ .forEach(intentService::withdraw);
+ }
+
+ @Override
+ public void submit(VplsOperation vplsOperation) {
+ if (isLeader) {
+ // Only leader can execute operation
+ addVplsOperation(vplsOperation);
+ }
+ }
+
+ /**
+ * Adds a VPLS operation to the queue of pending operations.
+ *
+ * @param vplsOperation the VPLS operation to add
+ */
+ private void addVplsOperation(VplsOperation vplsOperation) {
+ VplsData vplsData = vplsOperation.vpls();
+ pendingVplsOperations.compute(vplsData.name(), (name, opQueue) -> {
+ opQueue = opQueue == null ? Queues.newArrayDeque() : opQueue;
+
+ // If the operation already exist in queue, ignore it.
+ if (opQueue.contains(vplsOperation)) {
+ return opQueue;
+ }
+ opQueue.add(vplsOperation);
+ return opQueue;
+ });
+ }
+
+ /**
+ * Optimizes the VPLS operation queue and return a single VPLS operation to
+ * execute.
+ *
+ * @param operations the queue to be optimized
+ * @return optimized VPLS operation from the queue
+ */
+ protected static VplsOperation getOptimizedVplsOperation(Deque<VplsOperation> operations) {
+ if (operations.isEmpty()) {
+ return null;
+ }
+ // no need to optimize if the queue contains only one operation
+ if (operations.size() == 1) {
+ return operations.getFirst();
+ }
+
+ final VplsOperation firstOperation = operations.peekFirst();
+ final VplsOperation lastOperation = operations.peekLast();
+ final VplsOperation.Operation firstOp = firstOperation.op();
+ final VplsOperation.Operation lastOp = lastOperation.op();
+
+ if (firstOp.equals(VplsOperation.Operation.REMOVE)) {
+ if (lastOp.equals(VplsOperation.Operation.REMOVE)) {
+ // case 1: both first and last operation are REMOVE; do remove
+ return firstOperation;
+ } else if (lastOp.equals(VplsOperation.Operation.ADD)) {
+ // case 2: if first is REMOVE, and last is ADD; do update
+ return VplsOperation.of(lastOperation.vpls(),
+ VplsOperation.Operation.UPDATE);
+ } else {
+ // case 3: first is REMOVE, last is UPDATE; do update
+ return lastOperation;
+ }
+ } else if (firstOp.equals(VplsOperation.Operation.ADD)) {
+ if (lastOp.equals(VplsOperation.Operation.REMOVE)) {
+ // case 4: first is ADD, last is REMOVE; nothing to do
+ return null;
+ } else if (lastOp.equals(VplsOperation.Operation.ADD)) {
+ // case 5: both first and last are ADD, do add
+ return VplsOperation.of(lastOperation.vpls(),
+ VplsOperation.Operation.ADD);
+ } else {
+ // case 6: first is ADD and last is update, do add
+ return VplsOperation.of(lastOperation.vpls(),
+ VplsOperation.Operation.ADD);
+ }
+ } else {
+ if (lastOp.equals(VplsOperation.Operation.REMOVE)) {
+ // case 7: last is remove, do remove
+ return lastOperation;
+ } else if (lastOp.equals(VplsOperation.Operation.ADD)) {
+ // case 8: do update only
+ return VplsOperation.of(lastOperation.vpls(),
+ VplsOperation.Operation.UPDATE);
+ } else {
+ // case 9: from UPDATE to UPDATE
+ // only need last UPDATE operation
+ return VplsOperation.of(lastOperation.vpls(),
+ VplsOperation.Operation.UPDATE);
+ }
+ }
+ }
+
+ /**
+ * Scheduler for VPLS operation.
+ * Processes a batch of VPLS operations in a period.
+ */
+ class VplsOperationScheduler implements Runnable {
+ private static final String UNKNOWN_STATE =
+ "Unknown state {} for success consumer";
+ private static final String OP_EXEC_ERR =
+ "Error when executing VPLS operation {}, error: {}";
+
+ /**
+ * Process a batch of VPLS operations.
+ */
+ @Override
+ public void run() {
+ Set<String> vplsNames = pendingVplsOperations.keySet();
+ vplsNames.forEach(vplsName -> {
+ VplsOperation operation;
+ synchronized (runningOperations) {
+ // Only one operation for a VPLS at the same time
+ if (runningOperations.containsKey(vplsName)) {
+ return;
+ }
+ Deque<VplsOperation> operations = pendingVplsOperations.remove(vplsName);
+ operation = getOptimizedVplsOperation(operations);
+ if (operation == null) {
+ // Nothing to do, this only happened when we add a VPLS
+ // and remove it before batch operations been processed.
+ return;
+ }
+ runningOperations.put(vplsName, operation);
+ }
+
+ VplsOperationExecutor operationExecutor =
+ new VplsOperationExecutor(operation);
+ operationExecutor.setConsumers(
+ (vplsOperation) -> {
+ // Success consumer
+ VplsData vplsData = vplsOperation.vpls();
+ log.debug("VPLS operation success: {}", vplsOperation);
+ switch (vplsData.state()) {
+ case ADDING:
+ case UPDATING:
+ vplsData.state(VplsData.VplsState.ADDED);
+ vplsStore.updateVpls(vplsData);
+ break;
+ case REMOVING:
+ // The VPLS information does not exists in
+ // store. No need to update the store.
+ break;
+ default:
+ log.warn(UNKNOWN_STATE, vplsData.state());
+ vplsData.state(VplsData.VplsState.FAILED);
+ vplsStore.updateVpls(vplsData);
+ break;
+ }
+ runningOperations.remove(vplsName);
+ },
+ (vplsOperationException) -> {
+ // Error consumer
+ VplsOperation vplsOperation =
+ vplsOperationException.vplsOperation();
+ log.debug("VPLS operation failed: {}", vplsOperation);
+ VplsData vplsData = vplsOperation.vpls();
+ vplsData.state(VplsData.VplsState.FAILED);
+ vplsStore.updateVpls(vplsData);
+ log.error(OP_EXEC_ERR,
+ vplsOperation.toString(),
+ vplsOperationException.getMessage());
+ runningOperations.remove(vplsName);
+ });
+ log.debug("Applying operation: {}", operation);
+ workerExecutor.execute(operationExecutor);
+ });
+ }
+ }
+
+ /**
+ * Direction for Intent installation.
+ */
+ private enum Direction {
+ ADD,
+ REMOVE
+ }
+
+ /**
+ * VPLS operation executor.
+ * Installs, updates or removes Intents according to the given VPLS operation.
+ */
+ class VplsOperationExecutor implements Runnable {
+ private static final String UNKNOWN_OP = "Unknown operation.";
+ private static final String UNKNOWN_INTENT_DIR = "Unknown Intent install direction.";
+ private static final int OPERATION_TIMEOUT = 10;
+ private VplsOperation vplsOperation;
+ private Consumer<VplsOperation> successConsumer;
+ private Consumer<VplsOperationException> errorConsumer;
+ private VplsOperationException error;
+
+ public VplsOperationExecutor(VplsOperation vplsOperation) {
+ this.vplsOperation = vplsOperation;
+ this.error = null;
+ }
+
+ /**
+ * Sets success consumer and error consumer for this executor.
+ *
+ * @param successConsumer the success consumer
+ * @param errorConsumer the error consumer
+ */
+ public void setConsumers(Consumer<VplsOperation> successConsumer,
+ Consumer<VplsOperationException> errorConsumer) {
+ this.successConsumer = successConsumer;
+ this.errorConsumer = errorConsumer;
+
+ }
+
+ @Override
+ public void run() {
+ switch (vplsOperation.op()) {
+ case ADD:
+ installVplsIntents();
+ break;
+ case REMOVE:
+ removeVplsIntents();
+ break;
+ case UPDATE:
+ updateVplsIntents();
+ break;
+ default:
+ this.error = new VplsOperationException(vplsOperation,
+ UNKNOWN_OP);
+ break;
+ }
+
+ if (this.error != null) {
+ errorConsumer.accept(this.error);
+ } else {
+ successConsumer.accept(vplsOperation);
+ }
+ }
+
+ /**
+ * Updates Intents of the VPLS.
+ */
+ private void updateVplsIntents() {
+ // check which part we need to update
+ // if we update host only, we don't need to reinstall
+ // every Intents
+ Set<Intent> intentsToInstall = Sets.newHashSet();
+ Set<Intent> intentsToUninstall = Sets.newHashSet();
+ VplsData vplsData = vplsOperation.vpls();
+ Set<Intent> currentIntents = getCurrentIntents();
+
+ // Compares broadcast Intents
+ Set<Intent> currentBrcIntents = currentIntents.stream()
+ .filter(intent -> intent instanceof SinglePointToMultiPointIntent)
+ .collect(Collectors.toSet());
+ Set<Intent> targetBrcIntents = VplsIntentUtility.buildBrcIntents(vplsData, appId);
+ if (!intentSetEquals(currentBrcIntents, targetBrcIntents)) {
+ // If broadcast Intents changes, it means some network
+ // interfaces or encapsulation constraint changed; Need to
+ // reinstall all intents
+ removeVplsIntents();
+ installVplsIntents();
+ return;
+ }
+
+ // Compares unicast Intents
+ Set<Intent> currentUniIntents = currentIntents.stream()
+ .filter(intent -> intent instanceof MultiPointToSinglePointIntent)
+ .collect(Collectors.toSet());
+ Set<Intent> targetUniIntents = VplsIntentUtility.buildUniIntents(vplsData,
+ hostsFromVpls(),
+ appId);
+
+ // New unicast Intents to install
+ targetUniIntents.forEach(intent -> {
+ if (!currentUniIntents.contains(intent)) {
+ intentsToInstall.add(intent);
+ }
+ });
+
+ // Old unicast Intents to remove
+ currentUniIntents.forEach(intent -> {
+ if (!targetUniIntents.contains(intent)) {
+ intentsToUninstall.add(intent);
+ }
+ });
+ applyIntentsSync(intentsToUninstall, Direction.REMOVE);
+ applyIntentsSync(intentsToInstall, Direction.ADD);
+ }
+
+ private Set<Host> hostsFromVpls() {
+ VplsData vplsData = vplsOperation.vpls();
+ Set<Interface> interfaces = vplsData.interfaces();
+ return interfaces.stream()
+ .map(this::hostsFromInterface)
+ .flatMap(Collection::stream)
+ .collect(Collectors.toSet());
+ }
+
+ private Set<Host> hostsFromInterface(Interface iface) {
+ return hostService.getConnectedHosts(iface.connectPoint())
+ .stream()
+ .filter(host -> host.vlan().equals(iface.vlan()))
+ .collect(Collectors.toSet());
+ }
+
+ /**
+ * Applies Intents synchronously with a specific direction.
+ *
+ * @param intents the Intents
+ * @param direction the direction
+ */
+ private void applyIntentsSync(Set<Intent> intents, Direction direction) {
+ Set<Key> pendingIntentKeys = intents.stream()
+ .map(Intent::key).collect(Collectors.toSet());
+ IntentCompleter completer;
+
+ switch (direction) {
+ case ADD:
+ completer = new IntentCompleter(pendingIntentKeys,
+ IntentEvent.Type.INSTALLED);
+ intentService.addListener(completer);
+ intents.forEach(intentService::submit);
+ break;
+ case REMOVE:
+ completer = new IntentCompleter(pendingIntentKeys,
+ IntentEvent.Type.WITHDRAWN);
+ intentService.addListener(completer);
+ intents.forEach(intentService::withdraw);
+ break;
+ default:
+ this.error = new VplsOperationException(this.vplsOperation,
+ UNKNOWN_INTENT_DIR);
+ return;
+ }
+
+ try {
+ // Wait until Intent operation completed
+ completer.complete();
+ } catch (VplsOperationException e) {
+ this.error = e;
+ } finally {
+ intentService.removeListener(completer);
+ }
+ }
+
+ /**
+ * Checks if two sets of Intents are equal.
+ *
+ * @param intentSet1 the first set of Intents
+ * @param intentSet2 the second set of Intents
+ * @return true if both set of Intents are equal; otherwise false
+ */
+ private boolean intentSetEquals(Set<Intent> intentSet1, Set<Intent> intentSet2) {
+ if (intentSet1.size() != intentSet2.size()) {
+ return false;
+ }
+ for (Intent intent1 : intentSet1) {
+ if (intentSet2.stream()
+ .noneMatch(intent2 -> IntentUtils.intentsAreEqual(intent1, intent2))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Retrieves installed Intents from IntentService which related to
+ * specific VPLS.
+ *
+ * @return the Intents which related to the VPLS
+ */
+ private Set<Intent> getCurrentIntents() {
+ VplsData vplsData = vplsOperation.vpls();
+ String vplsName = vplsData.name();
+ return Tools.stream(intentService.getIntents())
+ .filter(intent -> intent.key().toString().startsWith(vplsName))
+ .collect(Collectors.toSet());
+ }
+
+ /**
+ * Generates unicast Intents and broadcast Intents for the VPLS.
+ *
+ * @return Intents for the VPLS
+ */
+ private Set<Intent> generateVplsIntents() {
+ VplsData vplsData = vplsOperation.vpls();
+ Set<Intent> brcIntents = VplsIntentUtility.buildBrcIntents(vplsData, appId);
+ Set<Intent> uniIntent = VplsIntentUtility.buildUniIntents(vplsData, hostsFromVpls(), appId);
+
+ return Stream.concat(brcIntents.stream(), uniIntent.stream())
+ .collect(Collectors.toSet());
+ }
+
+ /**
+ * Removes all Intents from the VPLS.
+ */
+ private void removeVplsIntents() {
+ Set<Intent> intentsToWithdraw = getCurrentIntents();
+ applyIntentsSync(intentsToWithdraw, Direction.REMOVE);
+ }
+
+ /**
+ * Installs Intents of the VPLS.
+ */
+ private void installVplsIntents() {
+ Set<Intent> intentsToInstall = generateVplsIntents();
+ applyIntentsSync(intentsToInstall, Direction.ADD);
+ }
+
+ /**
+ * Helper class which monitors if all Intent operations are completed.
+ */
+ class IntentCompleter implements IntentListener {
+ private static final String INTENT_COMPILE_ERR = "Got {} from intent completer";
+ private CompletableFuture<Void> completableFuture;
+ private Set<Key> pendingIntentKeys;
+ private IntentEvent.Type expectedEventType;
+
+ /**
+ * Initialize completer with given Intent keys and expect Intent
+ * event type.
+ *
+ * @param pendingIntentKeys the Intent keys to wait
+ * @param expectedEventType expect Intent event type
+ */
+ public IntentCompleter(Set<Key> pendingIntentKeys, IntentEvent.Type expectedEventType) {
+ this.completableFuture = new CompletableFuture<>();
+ this.pendingIntentKeys = Sets.newConcurrentHashSet(pendingIntentKeys);
+ this.expectedEventType = expectedEventType;
+ }
+
+ @Override
+ public void event(IntentEvent event) {
+ Intent intent = event.subject();
+ // Intent failed, throw an exception to completable future
+ if (event.type() == IntentEvent.Type.CORRUPT ||
+ event.type() == IntentEvent.Type.FAILED) {
+ completableFuture.completeExceptionally(new IntentException(intent.toString()));
+ return;
+ }
+ // If event type matched to expected type, remove from pending
+ if (event.type() == expectedEventType) {
+ Key key = intent.key();
+ pendingIntentKeys.remove(key);
+ }
+ if (pendingIntentKeys.isEmpty()) {
+ completableFuture.complete(null);
+ }
+ }
+
+ /**
+ * Waits until all pending Intents completed ot timeout.
+ */
+ public void complete() {
+ // If no pending Intent keys, complete directly
+ if (pendingIntentKeys.isEmpty()) {
+ return;
+ }
+ try {
+ completableFuture.get(OPERATION_TIMEOUT, TimeUnit.SECONDS);
+ } catch (TimeoutException | InterruptedException |
+ ExecutionException | IntentException e) {
+ // TODO: handle errors more carefully
+ log.warn(INTENT_COMPILE_ERR, e.toString());
+ throw new VplsOperationException(vplsOperation, e.toString());
+ }
+ }
+ }
+ }
+
+ /**
+ * A listener for leadership events.
+ * Only the leader can process VPLS operation in the ONOS cluster.
+ */
+ private class InternalLeadershipListener implements LeadershipEventListener {
+ private static final String LEADER_CHANGE = "Change leader to {}";
+
+ @Override
+ public void event(LeadershipEvent event) {
+ switch (event.type()) {
+ case LEADER_CHANGED:
+ case LEADER_AND_CANDIDATES_CHANGED:
+ isLeader = localNodeId.equals(event.subject().leaderNodeId());
+ if (isLeader) {
+ log.debug(LEADER_CHANGE, localNodeId);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ @Override
+ public boolean isRelevant(LeadershipEvent event) {
+ return event.subject().topic().equals(appId.name());
+ }
+ }
+}
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/api/Vpls.java b/apps/vpls/src/main/java/org/onosproject/vpls/api/Vpls.java
new file mode 100644
index 0000000..5dec2b2
--- /dev/null
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/api/Vpls.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2017-present 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.vpls.api;
+
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.net.EncapsulationType;
+
+import java.util.Collection;
+
+public interface Vpls {
+
+ /**
+ * Creates a new VPLS.
+ *
+ * @param vplsName the name of the VPLS
+ * @param encapsulationType the encapsulation type
+ * @return a VPLS instance if the operation is successful; null otherwise
+ */
+ VplsData createVpls(String vplsName, EncapsulationType encapsulationType);
+
+ /**
+ * Removes a VPLS.
+ *
+ * @param vplsData the VPLS to be removed
+ * @return the VPLS removed if the operation is successful; null otherwise
+ */
+ VplsData removeVpls(VplsData vplsData);
+
+ /**
+ * Adds network interfaces to a VPLS.
+ *
+ * @param vplsData the VPLS to which the interfaces have to be added to
+ * @param interfaces the interfaces to add
+ */
+ void addInterfaces(VplsData vplsData, Collection<Interface> interfaces);
+
+ /**
+ * Adds a network interface to a VPLS.
+ *
+ * @param vplsData the VPLS to which the interface has to be added to
+ * @param iface the interface to add
+ */
+ void addInterface(VplsData vplsData, Interface iface);
+
+ /**
+ * Sets an encapsulation type for a VPLS.
+ *
+ * @param vplsData the VPLS for which the encapsulation has to be set
+ * @param encapsulationType the encapsulation type
+ */
+ void setEncapsulationType(VplsData vplsData, EncapsulationType encapsulationType);
+
+ /**
+ * Retrieves a VPLS.
+ *
+ * @param vplsName the name of the VPLS
+ * @return the VPLS instance if the VPLS exists; null otherwise
+ */
+ VplsData getVpls(String vplsName);
+
+ /**
+ * Gets all VPLSs.
+ *
+ * @return a collection of VPLSs
+ */
+ Collection<VplsData> getAllVpls();
+
+ /**
+ * Removes the interfaces specified from a VPLS.
+ *
+ * @param vplsData the VPLS from which the interfaces are to be removed
+ * @param interfaces the interfaces to remove
+ * @return the interfaces removed
+ */
+ Collection<Interface> removeInterfaces(VplsData vplsData, Collection<Interface> interfaces);
+
+ /**
+ * Removes the interface specified from a VPLS.
+ *
+ * @param vplsData the VPLS from which the interface is to be removed
+ * @param iface the interface to remove
+ * @return the interface removed
+ */
+ Interface removeInterface(VplsData vplsData, Interface iface);
+
+ /**
+ * Removes all VPLSs and cleans up the VPLS configuration.
+ */
+ void removeAllVpls();
+}
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/api/VplsData.java b/apps/vpls/src/main/java/org/onosproject/vpls/api/VplsData.java
new file mode 100644
index 0000000..bba9d78
--- /dev/null
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/api/VplsData.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2017-present 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.vpls.api;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.net.EncapsulationType;
+
+import java.util.Collection;
+import java.util.Objects;
+import java.util.Set;
+
+import static java.util.Objects.*;
+
+/**
+ * Class stores a VPLS information.
+ */
+public final class VplsData {
+ /**
+ * States of a VPLS.
+ */
+ public enum VplsState {
+ UPDATING,
+ ADDING,
+ REMOVING,
+ ADDED,
+ REMOVED,
+ FAILED
+ }
+
+ private String name;
+ private Set<Interface> interfaces;
+ private EncapsulationType encapsulationType;
+ private VplsState state;
+
+ /**
+ * Constructs a VPLS data by given name and encapsulation type.
+ *
+ * @param name the given name
+ * @param encapType the encapsulation type
+ */
+ private VplsData(String name, EncapsulationType encapType) {
+ this.name = name;
+ this.encapsulationType = encapType;
+ this.interfaces = Sets.newHashSet();
+ this.state = VplsState.ADDING;
+ }
+
+ /**
+ * Creates a VPLS data by given name.
+ * The encapsulation type of the VPLS will be NONE.
+ *
+ * @param name the given name
+ * @return the VPLS data
+ */
+ public static VplsData of(String name) {
+ requireNonNull(name);
+ return new VplsData(name, EncapsulationType.NONE);
+ }
+
+ /**
+ * Creates a VPLS data by given name and encapsulation type.
+ *
+ * @param name the given name
+ * @param encapType the encapsulation type
+ * @return the VPLS data
+ */
+ public static VplsData of(String name, EncapsulationType encapType) {
+ requireNonNull(name);
+ if (encapType == null) {
+ return new VplsData(name, EncapsulationType.NONE);
+ } else {
+ return new VplsData(name, encapType);
+ }
+ }
+
+ /**
+ * Creates a copy of VPLS data.
+ *
+ * @param vplsData the VPLS data
+ * @return the copy of the VPLS data
+ */
+ public static VplsData of(VplsData vplsData) {
+ requireNonNull(vplsData);
+ VplsData vplsDataCopy = new VplsData(vplsData.name(), vplsData.encapsulationType());
+ vplsDataCopy.addInterfaces(vplsData.interfaces());
+ return vplsData;
+ }
+
+ /**
+ * Gets name of the VPLS.
+ *
+ * @return the name of the VPLS
+ */
+ public String name() {
+ return name;
+ }
+
+ public Set<Interface> interfaces() {
+ return ImmutableSet.copyOf(interfaces);
+ }
+
+ public EncapsulationType encapsulationType() {
+ return encapsulationType;
+ }
+
+ public void addInterfaces(Collection<Interface> interfaces) {
+ requireNonNull(interfaces);
+ this.interfaces.addAll(interfaces);
+ }
+
+ public void addInterface(Interface iface) {
+ requireNonNull(iface);
+ this.interfaces.add(iface);
+ }
+
+ public void removeInterfaces(Collection<Interface> interfaces) {
+ requireNonNull(interfaces);
+ this.interfaces.removeAll(interfaces);
+ }
+
+ public void removeInterface(Interface iface) {
+ requireNonNull(iface);
+ this.interfaces.remove(iface);
+ }
+
+ public void encapsulationType(EncapsulationType encapType) {
+ this.encapsulationType = encapType;
+ }
+
+ public VplsState state() {
+ return state;
+ }
+
+ public void state(VplsState state) {
+ this.state = state;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("name", name)
+ .add("interfaces", interfaces)
+ .add("encap type", encapsulationType)
+ .add("state", state)
+ .toString();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (!(obj instanceof VplsData)) {
+ return false;
+ }
+ VplsData other = (VplsData) obj;
+ return Objects.equals(other.name, this.name) &&
+ Objects.equals(other.interfaces, this.interfaces) &&
+ Objects.equals(other.encapsulationType, this.encapsulationType);
+ }
+
+ @Override
+ public int hashCode() {
+ return hash(name, interfaces, encapsulationType);
+ }
+}
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/api/VplsOperation.java b/apps/vpls/src/main/java/org/onosproject/vpls/api/VplsOperation.java
new file mode 100644
index 0000000..abc5025
--- /dev/null
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/api/VplsOperation.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2017-present 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.vpls.api;
+
+import com.google.common.base.MoreObjects;
+
+import java.util.Objects;
+
+import static java.util.Objects.*;
+
+/**
+ * Operation for VPLS.
+ */
+public class VplsOperation {
+
+ /**
+ * The operation type.
+ */
+ public enum Operation {
+ REMOVE,
+ ADD,
+ UPDATE
+ }
+
+ private VplsData vplsData;
+ private Operation op;
+
+ /**
+ * Defines a VPLS operation by binding a given VPLS and operation type.
+ *
+ * @param vplsData the VPLS
+ * @param op the operation
+ */
+ protected VplsOperation(VplsData vplsData, Operation op) {
+ requireNonNull(vplsData);
+ requireNonNull(op);
+ // Make a copy of the VPLS data to ensure other thread won't change it.
+ this.vplsData = VplsData.of(vplsData);
+ this.op = op;
+ }
+
+ /**
+ * Defines a VPLS operation by binding a given VPLS and operation type.
+ *
+ * @param vplsData the VPLS
+ * @param op the operation
+ * @return the VPLS operation
+ */
+ public static VplsOperation of(VplsData vplsData, Operation op) {
+ return new VplsOperation(vplsData, op);
+ }
+
+ /**
+ * Retrieves the VPLS from the operation.
+ *
+ * @return the VPLS
+ */
+ public VplsData vpls() {
+ return vplsData;
+ }
+
+ /**
+ * Retrieves the operation type from the operation.
+ *
+ * @return the operation type
+ */
+ public Operation op() {
+ return op;
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("vplsData", vplsData.toString())
+ .add("op", op.toString())
+ .toString();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (!(obj instanceof VplsOperation)) {
+ return false;
+ }
+ VplsOperation other = (VplsOperation) obj;
+ return Objects.equals(other.vplsData, this.vplsData) &&
+ Objects.equals(other.op, this.op);
+ }
+
+ @Override
+ public int hashCode() {
+ return hash(vplsData, op);
+ }
+}
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/api/VplsOperationException.java b/apps/vpls/src/main/java/org/onosproject/vpls/api/VplsOperationException.java
new file mode 100644
index 0000000..95dfdfa
--- /dev/null
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/api/VplsOperationException.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2017-present 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.vpls.api;
+
+/**
+ * Exception for VPLS operation.
+ */
+public class VplsOperationException extends RuntimeException {
+ private static final long serialVersionUID = 4514685940685335886L;
+ private VplsOperation vplsOperation;
+
+ /**
+ * Constructs a VPLS operation exception with given VPLS operation and
+ * message.
+ *
+ * @param operation the VPLS operation
+ * @param msg the description of the exception
+ */
+ public VplsOperationException(VplsOperation operation, String msg) {
+ super(msg);
+ this.vplsOperation = operation;
+ }
+
+ /**
+ * Gets VPLS operation for this exception.
+ * @return the VPLS operation
+ */
+ public VplsOperation vplsOperation() {
+ return vplsOperation;
+ }
+}
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/config/impl/package-info.java b/apps/vpls/src/main/java/org/onosproject/vpls/api/VplsOperationService.java
similarity index 62%
copy from apps/vpls/src/main/java/org/onosproject/vpls/config/impl/package-info.java
copy to apps/vpls/src/main/java/org/onosproject/vpls/api/VplsOperationService.java
index b5d6ec9..3044f7e 100644
--- a/apps/vpls/src/main/java/org/onosproject/vpls/config/impl/package-info.java
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/api/VplsOperationService.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-present Open Networking Laboratory
+ * Copyright 2017-present 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.
@@ -14,7 +14,17 @@
* limitations under the License.
*/
+package org.onosproject.vpls.api;
+
/**
- * Configuration service implementation for VPLS.
+ * Service for application submitting VPLS operation.
*/
-package org.onosproject.vpls.config.impl;
+public interface VplsOperationService {
+
+ /**
+ * Submits a VPLS operation.
+ *
+ * @param vplsOperation the VPLS operation to submit
+ */
+ void submit(VplsOperation vplsOperation);
+}
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/api/VplsStore.java b/apps/vpls/src/main/java/org/onosproject/vpls/api/VplsStore.java
new file mode 100644
index 0000000..caf7550
--- /dev/null
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/api/VplsStore.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2017-present 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.vpls.api;
+
+import org.onosproject.store.Store;
+import org.onosproject.store.StoreDelegate;
+import org.onosproject.vpls.store.VplsStoreEvent;
+
+import java.util.Collection;
+
+/**
+ * Definition of the operations regarding the management of the VPLS elements.
+ */
+public interface VplsStore extends Store<VplsStoreEvent, StoreDelegate<VplsStoreEvent>> {
+ /**
+ * Adds a VPLS to the configuration.
+ *
+ * @param vplsData the VPLS to add
+ */
+ void addVpls(VplsData vplsData);
+
+ /**
+ * Removes a VPLS from the configuration.
+ *
+ * @param vplsData the VPLS to remove
+ */
+ void removeVpls(VplsData vplsData);
+
+ /**
+ * Updates a VPLS.
+ *
+ * @param vplsData the VPLS to update
+ */
+ void updateVpls(VplsData vplsData);
+
+ /**
+ * Gets a VPLS by name.
+ *
+ * @param vplsName the VPLS name
+ * @return the VPLS instance if it exists; null otherwise
+ */
+ VplsData getVpls(String vplsName);
+
+ /**
+ * Gets all the VPLSs.
+ *
+ * @return all the VPLSs
+ */
+ Collection<VplsData> getAllVpls();
+}
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/config/impl/package-info.java b/apps/vpls/src/main/java/org/onosproject/vpls/api/package-info.java
similarity index 79%
rename from apps/vpls/src/main/java/org/onosproject/vpls/config/impl/package-info.java
rename to apps/vpls/src/main/java/org/onosproject/vpls/api/package-info.java
index b5d6ec9..91d3937 100644
--- a/apps/vpls/src/main/java/org/onosproject/vpls/config/impl/package-info.java
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/api/package-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-present Open Networking Laboratory
+ * Copyright 2017-present 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.
@@ -15,6 +15,6 @@
*/
/**
- * Configuration service implementation for VPLS.
+ * APIs for VPLS.
*/
-package org.onosproject.vpls.config.impl;
+package org.onosproject.vpls.api;
\ No newline at end of file
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/cli/VplsCommand.java b/apps/vpls/src/main/java/org/onosproject/vpls/cli/VplsCommand.java
index d4bfa8d..4718453 100644
--- a/apps/vpls/src/main/java/org/onosproject/vpls/cli/VplsCommand.java
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/cli/VplsCommand.java
@@ -15,24 +15,24 @@
*/
package org.onosproject.vpls.cli;
-import com.google.common.collect.Lists;
-import com.google.common.collect.SetMultimap;
-import com.google.common.collect.Sets;
import org.apache.karaf.shell.commands.Argument;
import org.apache.karaf.shell.commands.Command;
import org.onosproject.cli.AbstractShellCommand;
import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.incubator.net.intf.InterfaceService;
import org.onosproject.net.EncapsulationType;
-import org.onosproject.vpls.config.VplsConfigService;
+import org.onosproject.vpls.api.VplsData;
+import org.onosproject.vpls.api.Vpls;
+import java.util.Collection;
import java.util.Collections;
import java.util.List;
-import java.util.Map;
-import java.util.Optional;
import java.util.Set;
+import java.util.stream.Collectors;
import static com.google.common.base.Strings.isNullOrEmpty;
+
/**
* CLI to interact with the VPLS application.
*/
@@ -85,7 +85,8 @@
" not found" + RESET;
private static final String VPLS_DISPLAY = "VPLS name: " + BOLD +
- "%s" + RESET + "\nAssociated interfaces: %s\nEncapsulation: %s";
+ "%s" + RESET + "\nAssociated interfaces: %s\nEncapsulation: %s\n" +
+ "State: %s";
private static final String VPLS_NOT_FOUND =
COLOR_ERROR + "VPLS " + BOLD + "%s" + RESET + COLOR_ERROR +
@@ -93,15 +94,13 @@
private static final String IFACE_NOT_ASSOCIATED =
COLOR_ERROR + "Interface " + BOLD + "%s" + RESET + COLOR_ERROR +
- " cannot be removed from VPLS " + BOLD + "%s" + RESET +
- COLOR_ERROR + ". The interface is associated to another" +
- "VPLS (" + BOLD + "%s" + RESET + COLOR_ERROR + ")" + RESET;
+ " cannot be removed from VPLS " + BOLD + "%s" + RESET + ".";
- private static VplsConfigService vplsConfigService =
- get(VplsConfigService.class);
+ protected static Vpls vpls;
+ protected static InterfaceService interfaceService;
@Argument(index = 0, name = "command", description = "Command name (add-if|" +
- "clean|create|delete|list|rem-if|set-encap|show)",
+ "create|delete|list|rem-if|set-encap|show)",
required = true, multiValued = false)
String command = null;
@@ -116,15 +115,19 @@
@Override
protected void execute() {
+ if (vpls == null) {
+ vpls = get(Vpls.class);
+ }
+ if (interfaceService == null) {
+ interfaceService = get(InterfaceService.class);
+ }
+
VplsCommandEnum enumCommand = VplsCommandEnum.enumFromString(command);
if (enumCommand != null) {
switch (enumCommand) {
case ADD_IFACE:
addIface(vplsName, optArg);
break;
- case CLEAN:
- clean();
- break;
case CREATE:
create(vplsName);
break;
@@ -143,6 +146,9 @@
case SHOW:
show(vplsName);
break;
+ case CLEAN:
+ cleanVpls();
+ break;
default:
print(VPLS_COMMAND_NOT_FOUND, command);
}
@@ -157,7 +163,7 @@
* @param vplsName the name of the VLPS
* @param ifaceName the name of the interface to add
*/
- private void addIface(String vplsName, String ifaceName) {
+ protected void addIface(String vplsName, String ifaceName) {
if (vplsName == null) {
print(INSERT_VPLS_NAME);
return;
@@ -166,28 +172,24 @@
print(INSERT_INTERFACE);
return;
}
- if (!vplsExists(vplsName)) {
+
+ Interface iface = getInterface(ifaceName);
+ VplsData vplsData = vpls.getVpls(vplsName);
+
+ if (vplsData == null) {
print(VPLS_NOT_FOUND, vplsName);
return;
}
- if (!ifaceExists(ifaceName)) {
+ if (iface == null) {
print(IFACE_NOT_FOUND, ifaceName);
return;
}
- if (isIfaceAssociated(ifaceName)) {
+ if (isIfaceAssociated(iface)) {
print(IFACE_ALREADY_ASSOCIATED,
- ifaceName, vplsNameFromIfaceName(ifaceName));
+ ifaceName, getVplsByInterface(iface).name());
return;
}
-
- vplsConfigService.addIface(vplsName, ifaceName);
- }
-
- /**
- * Cleans the VPLS configuration.
- */
- private void clean() {
- vplsConfigService.cleanVplsConfig();
+ vpls.addInterface(vplsData, iface);
}
/**
@@ -195,16 +197,17 @@
*
* @param vplsName the name of the VLPS
*/
- private void create(String vplsName) {
+ protected void create(String vplsName) {
if (vplsName == null || vplsName.isEmpty()) {
print(INSERT_VPLS_NAME);
return;
}
- if (vplsExists(vplsName)) {
+ VplsData vplsData = vpls.getVpls(vplsName);
+ if (vplsData != null) {
print(VPLS_ALREADY_EXISTS, vplsName);
return;
}
- vplsConfigService.addVpls(vplsName, Sets.newHashSet(), null);
+ vpls.createVpls(vplsName, EncapsulationType.NONE);
}
/**
@@ -212,23 +215,26 @@
*
* @param vplsName the name of the VLPS
*/
- private void delete(String vplsName) {
+ protected void delete(String vplsName) {
if (vplsName == null) {
print(INSERT_VPLS_NAME);
return;
}
- if (!vplsExists(vplsName)) {
+ VplsData vplsData = vpls.getVpls(vplsName);
+ if (vplsData == null) {
print(VPLS_NOT_FOUND, vplsName);
return;
}
- vplsConfigService.removeVpls(vplsName);
+ vpls.removeVpls(vplsData);
}
/**
* Lists the configured VPLSs.
*/
- private void list() {
- List<String> vplsNames = Lists.newArrayList(vplsConfigService.vplsNames());
+ protected void list() {
+ List<String> vplsNames = vpls.getAllVpls().stream()
+ .map(VplsData::name)
+ .collect(Collectors.toList());
Collections.sort(vplsNames);
vplsNames.forEach(vpls -> {
@@ -242,7 +248,7 @@
* @param vplsName the name of the VLPS
* @param ifaceName the name of the interface to remove
*/
- private void removeIface(String vplsName, String ifaceName) {
+ protected void removeIface(String vplsName, String ifaceName) {
if (vplsName == null) {
print(INSERT_VPLS_NAME);
return;
@@ -251,20 +257,21 @@
print(INSERT_INTERFACE);
return;
}
- if (!vplsExists(vplsName)) {
+ VplsData vplsData = vpls.getVpls(vplsName);
+ Interface iface = getInterface(ifaceName);
+ if (vplsData == null) {
print(VPLS_NOT_FOUND, vplsName);
return;
}
- if (!ifaceExists(ifaceName)) {
+ if (iface == null) {
print(IFACE_NOT_FOUND, ifaceName);
return;
}
- String vplsNameFromIfaceName = vplsNameFromIfaceName(ifaceName);
- if (!vplsNameFromIfaceName.equals(vplsName)) {
- print(IFACE_NOT_ASSOCIATED, ifaceName, vplsName, vplsNameFromIfaceName);
+ if (!vplsData.interfaces().contains(iface)) {
+ print(IFACE_NOT_ASSOCIATED, ifaceName, vplsName);
return;
}
- vplsConfigService.removeIface(ifaceName);
+ vpls.removeInterface(vplsData, iface);
}
/**
@@ -273,7 +280,7 @@
* @param vplsName the name of the VPLS
* @param encap the encapsulation type
*/
- private void setEncap(String vplsName, String encap) {
+ protected void setEncap(String vplsName, String encap) {
if (vplsName == null) {
print(INSERT_VPLS_NAME);
return;
@@ -282,7 +289,8 @@
print(INSERT_ENCAP_TYPE);
return;
}
- if (!vplsExists(vplsName)) {
+ VplsData vplsData = vpls.getVpls(vplsName);
+ if (vplsData == null) {
print(VPLS_NOT_FOUND, vplsName);
return;
}
@@ -292,7 +300,7 @@
print(ENCAP_NOT_FOUND, encap);
return;
}
- vplsConfigService.setEncap(vplsName, encap);
+ vpls.setEncapsulationType(vplsData, encapType);
}
/**
@@ -300,113 +308,88 @@
*
* @param vplsName the name of the VPLS
*/
- private void show(String vplsName) {
- List<String> vplsNames = Lists.newArrayList(vplsConfigService.vplsNames());
- Collections.sort(vplsNames);
-
- Map<String, EncapsulationType> encapByVplsName =
- vplsConfigService.encapByVplsName();
-
+ protected void show(String vplsName) {
if (!isNullOrEmpty(vplsName)) {
// A VPLS name is provided. Check first if the VPLS exists
- if (vplsExists(vplsName)) {
+ VplsData vplsData = vpls.getVpls(vplsName);
+ if (vplsData != null) {
+ Set<String> ifaceNames = vplsData.interfaces().stream()
+ .map(Interface::name)
+ .collect(Collectors.toSet());
print(VPLS_DISPLAY,
vplsName,
- ifacesFromVplsName(vplsName).toString(),
- encapByVplsName.get(vplsName).toString());
+ ifaceNames,
+ vplsData.encapsulationType().toString(),
+ vplsData.state());
} else {
print(VPLS_NOT_FOUND, vplsName);
}
} else {
+ Collection<VplsData> vplses = vpls.getAllVpls();
// No VPLS names are provided. Display all VPLSs configured
print(SEPARATOR);
- vplsNames.forEach(v -> {
+ vplses.forEach(vplsData -> {
+ Set<String> ifaceNames = vplsData.interfaces().stream()
+ .map(Interface::name)
+ .collect(Collectors.toSet());
print(VPLS_DISPLAY,
- v,
- ifacesFromVplsName(v).toString(),
- encapByVplsName.get(v).toString());
+ vplsData.name(),
+ ifaceNames,
+ vplsData.encapsulationType().toString(),
+ vplsData.state());
print(SEPARATOR);
});
}
}
/**
- * States if a VPLS exists or not.
- *
- * @param vplsName the name of the VPLS
- * @return true if the VPLS exists; false otherwise
+ * Remove all VPLS.
*/
- private static boolean vplsExists(String vplsName) {
- return vplsConfigService.vplsNames().contains(vplsName);
+ protected void cleanVpls() {
+ vpls.removeAllVpls();
}
- /**
- * States if an interface is defined or not in the system.
- *
- * @param ifaceName the name of the interface
- * @return true if the interface is defined; false otherwise
- */
- private static boolean ifaceExists(String ifaceName) {
- return vplsConfigService.allIfaces()
- .stream()
- .anyMatch(iface -> iface.name().equals(ifaceName));
- }
/**
* States if an interface is already associated to a VPLS.
*
- * @param ifaceName the name of the interface
+ * @param iface the interface
* @return true if the interface is already associated to a VPLS; false
* otherwise
*/
- private static boolean isIfaceAssociated(String ifaceName) {
- return vplsConfigService.ifaces()
+ private static boolean isIfaceAssociated(Interface iface) {
+ return vpls.getAllVpls()
.stream()
- .anyMatch(iface -> iface.name().equals(ifaceName));
+ .map(VplsData::interfaces)
+ .flatMap(Collection::stream)
+ .anyMatch(iface::equals);
}
/**
- * Returns the name of a VPLS, given the name of an interface associated to
- * it.
+ * Gets a network interface by given interface name.
*
- * @param ifaceName the name of the interface
- * @return the name of the VPLS that has the interface configured; null if
- * the interface does not exist or is not associated to any VPLS
+ * @param interfaceName the interface name
+ * @return the network interface
*/
- private static String vplsNameFromIfaceName(String ifaceName) {
- String vplsName = null;
-
- Optional<String> optVplsName = vplsConfigService.ifacesByVplsName()
- .entries()
- .stream()
- .filter((entry -> entry.getValue().name().equals(ifaceName)))
- .map(Map.Entry::getKey)
- .findFirst();
-
- if (optVplsName.isPresent()) {
- vplsName = optVplsName.get();
- }
-
- return vplsName;
+ private Interface getInterface(String interfaceName) {
+ // FIXME: only returns first interface it found
+ // multiple interface with same name not support
+ return interfaceService.getInterfaces().stream()
+ .filter(iface -> iface.name().equals(interfaceName))
+ .findFirst()
+ .orElse(null);
}
/**
- * Returns a list of interfaces associated to a VPLS, given a VPLS name.
+ * Gets a VPLS related to the network interface.
*
- * @param vplsName the name of the VPLS
- * @return the set of interfaces associated to the given VPLS; null if the
- * VPLS is not found
+ * @param iface the network interface
+ * @return the VPLS related to the network interface
*/
- private static Set<String> ifacesFromVplsName(String vplsName) {
- if (!vplsExists(vplsName)) {
- return null;
- }
- SetMultimap<String, Interface> ifacesByVplsName =
- vplsConfigService.ifacesByVplsName();
- Set<String> ifaceNames = Sets.newHashSet();
-
- ifacesByVplsName.get(vplsName).forEach(iface -> ifaceNames.add(iface.name()));
-
- return ifaceNames;
+ private VplsData getVplsByInterface(Interface iface) {
+ return vpls.getAllVpls().stream()
+ .filter(vplsData -> vplsData.interfaces().contains(iface))
+ .findFirst()
+ .orElse(null);
}
}
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/cli/VplsCommandEnum.java b/apps/vpls/src/main/java/org/onosproject/vpls/cli/VplsCommandEnum.java
index dfdaadf..c1ccb2b 100644
--- a/apps/vpls/src/main/java/org/onosproject/vpls/cli/VplsCommandEnum.java
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/cli/VplsCommandEnum.java
@@ -25,13 +25,13 @@
*/
public enum VplsCommandEnum {
ADD_IFACE("add-if"),
- CLEAN("clean"),
CREATE("create"),
DELETE("delete"),
LIST("list"),
REMOVE_IFACE("rem-if"),
SET_ENCAP("set-encap"),
- SHOW("show");
+ SHOW("show"),
+ CLEAN("clean");
private final String command;
@@ -40,7 +40,7 @@
*
* @param command the text representing the command
*/
- private VplsCommandEnum(final String command) {
+ VplsCommandEnum(final String command) {
this.command = command;
}
@@ -56,7 +56,7 @@
*/
public static List<String> toStringList() {
return Arrays.stream(values())
- .map(c -> c.toString())
+ .map(VplsCommandEnum::toString)
.collect(Collectors.toCollection(ArrayList::new));
}
@@ -71,7 +71,7 @@
public static VplsCommandEnum enumFromString(String command) {
if (command != null && !command.isEmpty()) {
for (VplsCommandEnum c : values()) {
- if (command.toString().equalsIgnoreCase(c.toString())) {
+ if (command.equalsIgnoreCase(c.toString())) {
return c;
}
}
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/cli/completer/VplsCommandCompleter.java b/apps/vpls/src/main/java/org/onosproject/vpls/cli/completer/VplsCommandCompleter.java
index cfe08b7..2b198a9 100644
--- a/apps/vpls/src/main/java/org/onosproject/vpls/cli/completer/VplsCommandCompleter.java
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/cli/completer/VplsCommandCompleter.java
@@ -16,6 +16,7 @@
package org.onosproject.vpls.cli.completer;
import com.google.common.collect.Lists;
+import org.apache.karaf.shell.console.completer.ArgumentCompleter;
import org.onosproject.cli.AbstractChoicesCompleter;
import org.onosproject.vpls.cli.VplsCommandEnum;
@@ -29,17 +30,19 @@
@Override
public List<String> choices() {
- List<String> argumentList =
- Lists.newArrayList(getArgumentList().getArguments());
+ ArgumentCompleter.ArgumentList argumentList = getArgumentList();
+ if (argumentList == null) {
+ return Collections.emptyList();
+ }
+ List<String> argList = Lists.newArrayList(argumentList.getArguments());
String argOne = null;
- if (argumentList.size() > 1) {
- argOne = argumentList.get(1);
+ if (argList.size() > 1) {
+ argOne = argList.get(1);
}
VplsCommandEnum vplsCommandEnum = VplsCommandEnum.enumFromString(argOne);
if (vplsCommandEnum != null) {
switch (vplsCommandEnum) {
case CREATE:
- case CLEAN:
case LIST:
return Collections.emptyList();
default:
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/cli/completer/VplsNameCompleter.java b/apps/vpls/src/main/java/org/onosproject/vpls/cli/completer/VplsNameCompleter.java
index ab2479b..6900880 100644
--- a/apps/vpls/src/main/java/org/onosproject/vpls/cli/completer/VplsNameCompleter.java
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/cli/completer/VplsNameCompleter.java
@@ -15,11 +15,13 @@
*/
package org.onosproject.vpls.cli.completer;
-import com.google.common.collect.Lists;
import org.onosproject.cli.AbstractChoicesCompleter;
-import org.onosproject.vpls.config.VplsConfigService;
+import org.onosproject.vpls.api.Vpls;
+import org.onosproject.vpls.api.VplsData;
+import java.util.Collection;
import java.util.List;
+import java.util.stream.Collectors;
import static org.onosproject.cli.AbstractShellCommand.get;
@@ -28,9 +30,14 @@
*/
public class VplsNameCompleter extends AbstractChoicesCompleter {
+ protected static Vpls vpls;
+
@Override
public List<String> choices() {
- VplsConfigService vplsConfigService = get(VplsConfigService.class);
- return Lists.newArrayList(vplsConfigService.vplsNames());
+ if (vpls == null) {
+ vpls = get(Vpls.class);
+ }
+ Collection<VplsData> vplses = vpls.getAllVpls();
+ return vplses.stream().map(VplsData::name).collect(Collectors.toList());
}
}
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/cli/completer/VplsOptArgCompleter.java b/apps/vpls/src/main/java/org/onosproject/vpls/cli/completer/VplsOptArgCompleter.java
index 5b60352..7058cdc 100644
--- a/apps/vpls/src/main/java/org/onosproject/vpls/cli/completer/VplsOptArgCompleter.java
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/cli/completer/VplsOptArgCompleter.java
@@ -16,15 +16,17 @@
package org.onosproject.vpls.cli.completer;
import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
import org.apache.karaf.shell.console.completer.ArgumentCompleter;
import org.onosproject.cli.AbstractChoicesCompleter;
import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.incubator.net.intf.InterfaceService;
import org.onosproject.net.EncapsulationType;
+import org.onosproject.vpls.api.Vpls;
+import org.onosproject.vpls.api.VplsData;
import org.onosproject.vpls.cli.VplsCommandEnum;
-import org.onosproject.vpls.config.VplsConfigService;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -36,22 +38,29 @@
* VPLS optional argument completer.
*/
public class VplsOptArgCompleter extends AbstractChoicesCompleter {
+ protected static Vpls vpls;
+ protected static InterfaceService interfaceService;
@Override
public List<String> choices() {
- VplsConfigService vplsConfigService = get(VplsConfigService.class);
- List<String> argumentList =
- Lists.newArrayList(getArgumentList().getArguments());
- String argOne = argumentList.get(1);
+ if (vpls == null) {
+ vpls = get(Vpls.class);
+ }
+ ArgumentCompleter.ArgumentList argumentList = getArgumentList();
+ if (argumentList == null) {
+ return Collections.emptyList();
+ }
+ List<String> argList = Lists.newArrayList(argumentList.getArguments());
+ String argOne = argList.get(1);
VplsCommandEnum vplsCommandEnum = VplsCommandEnum.enumFromString(argOne);
if (vplsCommandEnum != null) {
switch (vplsCommandEnum) {
case ADD_IFACE:
- return availableIfaces(vplsConfigService);
+ return availableIfaces();
case SET_ENCAP:
return encap();
case REMOVE_IFACE:
- return vplsIfaces(vplsConfigService);
+ return vplsIfaces();
default:
return Collections.emptyList();
}
@@ -64,21 +73,27 @@
*
* @return the list of interfaces not yet assigned to any VPLS
*/
- private List<String> availableIfaces(VplsConfigService vplsConfigService) {
- List<String> ifacesAvailable = Lists.newArrayList();
- Set<Interface> allIfaces = Sets.newHashSet(vplsConfigService.allIfaces());
- Set<Interface> usedIfaces = Sets.newHashSet(vplsConfigService.ifaces());
+ private List<String> availableIfaces() {
+ if (interfaceService == null) {
+ interfaceService = get(InterfaceService.class);
+ }
- allIfaces.removeAll(usedIfaces);
- allIfaces.forEach(iface -> ifacesAvailable.add(iface.name()));
+ Set<Interface> allIfaces = interfaceService.getInterfaces();
+ Set<Interface> usedIfaces = vpls.getAllVpls().stream()
+ .map(VplsData::interfaces)
+ .flatMap(Collection::stream)
+ .collect(Collectors.toSet());
- return ifacesAvailable;
+ return allIfaces.stream()
+ .filter(iface -> !usedIfaces.contains(iface))
+ .map(Interface::name)
+ .collect(Collectors.toList());
}
/**
* Returns the list of supported encapsulation types.
*
- * @return the list of supported encapsualtion types
+ * @return the list of supported encapsulation types
*/
private List<String> encap() {
return Arrays.stream(EncapsulationType.values())
@@ -91,15 +106,12 @@
*
* @return the list of interfaces associated to a VPLS
*/
- private List<String> vplsIfaces(VplsConfigService vplsConfigService) {
+ private List<String> vplsIfaces() {
ArgumentCompleter.ArgumentList list = getArgumentList();
String vplsName = list.getArguments()[2];
-
- List<String> vplsIfaces = Lists.newArrayList();
-
- Set<Interface> connectPoints = vplsConfigService.ifaces(vplsName);
- connectPoints.forEach(iface -> vplsIfaces.add(iface.name()));
-
- return vplsIfaces;
+ VplsData vplsData = vpls.getVpls(vplsName);
+ return vplsData.interfaces().stream()
+ .map(Interface::name)
+ .collect(Collectors.toList());
}
}
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/config/VplsAppConfig.java b/apps/vpls/src/main/java/org/onosproject/vpls/config/VplsAppConfig.java
index 66f7882..775d56b 100644
--- a/apps/vpls/src/main/java/org/onosproject/vpls/config/VplsAppConfig.java
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/config/VplsAppConfig.java
@@ -35,6 +35,9 @@
private static final String INTERFACE = "interfaces";
private static final String ENCAPSULATION = "encapsulation";
+ // Record update time when VPLS distribute store update it
+ private static final String UPDATE_TIME = "lastUpdateTime";
+
/**
* Returns a set of configured VPLSs.
*
@@ -42,9 +45,7 @@
*/
public Set<VplsConfig> vplss() {
Set<VplsConfig> vplss = Sets.newHashSet();
-
JsonNode vplsNode = object.get(VPLS);
-
if (vplsNode == null) {
return vplss;
}
@@ -67,7 +68,6 @@
ifaces,
EncapsulationType.enumFromString(encap)));
});
-
return vplss;
}
@@ -215,4 +215,37 @@
private ArrayNode initVplsConfiguration() {
return object.putArray(VPLS);
}
+
+ /**
+ * Sets the last time of update for the VPLS information in the store.
+ *
+ * @param timestamp the update time
+ */
+ public void updateTime(long timestamp) {
+ object.put(UPDATE_TIME, timestamp);
+ }
+
+ /**
+ * Retrieves the last time of update for the VPLS information in the store.
+ *
+ * @return update time, -1 if there is no update time in the config
+ */
+ public long updateTime() {
+ if (object.get(UPDATE_TIME) != null) {
+ return object.get(UPDATE_TIME).asLong();
+ } else {
+ // No update time
+ return -1L;
+ }
+ }
+
+ /**
+ * Clears all VPLS configurations.
+ */
+ public void clearVplsConfig() {
+ if (object.get(VPLS) != null) {
+ object.remove(VPLS);
+ }
+ initVplsConfiguration();
+ }
}
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/config/VplsConfigManager.java b/apps/vpls/src/main/java/org/onosproject/vpls/config/VplsConfigManager.java
new file mode 100644
index 0000000..71b14ad
--- /dev/null
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/config/VplsConfigManager.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright 2017-present 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.vpls.config;
+
+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.cluster.LeadershipService;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.incubator.net.intf.InterfaceService;
+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.basics.SubjectFactories;
+import org.onosproject.vpls.VplsManager;
+import org.onosproject.vpls.api.VplsData;
+import org.onosproject.vpls.api.Vpls;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+import static org.onlab.util.Tools.groupedThreads;
+
+/**
+ * Component for the management of the VPLS configuration.
+ */
+@Component(immediate = true)
+public class VplsConfigManager {
+ private static final Class<VplsAppConfig> CONFIG_CLASS = VplsAppConfig.class;
+ private static final String NET_CONF_EVENT = "Received NetworkConfigEvent {}";
+ private static final String CONFIG_NULL = "VPLS configuration not defined";
+ private static final int INITIAL_RELOAD_CONFIG_DELAY = 0;
+ private static final int INITIAL_RELOAD_CONFIG_PERIOD = 1000;
+ private static final int NUM_THREADS = 1;
+ private static final String VPLS = "vpls";
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private final NetworkConfigListener configListener =
+ new InternalNetworkConfigListener();
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected NetworkConfigService configService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected InterfaceService interfaceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected NetworkConfigRegistry registry;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected Vpls vpls;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LeadershipService leadershipService;
+
+ private ScheduledExecutorService reloadExecutor =
+ Executors.newScheduledThreadPool(NUM_THREADS,
+ groupedThreads("onos/apps/vpls",
+ "config-reloader-%d",
+ log)
+ );
+
+ private ConfigFactory<ApplicationId, VplsAppConfig> vplsConfigFactory =
+ new ConfigFactory<ApplicationId, VplsAppConfig>(
+ SubjectFactories.APP_SUBJECT_FACTORY, VplsAppConfig.class, VPLS) {
+ @Override
+ public VplsAppConfig createConfig() {
+ return new VplsAppConfig();
+ }
+ };
+
+ protected ApplicationId appId;
+
+ @Activate
+ void activate() {
+ appId = coreService.registerApplication(VplsManager.VPLS_APP);
+ configService.addListener(configListener);
+
+ // Load config when VPLS service started and there is a leader for VPLS;
+ // otherwise, try again after a period.
+ reloadExecutor.scheduleAtFixedRate(() -> {
+ NodeId vplsLeaderNode = leadershipService.getLeader(appId.name());
+ if (vpls == null || vplsLeaderNode == null) {
+ return;
+ }
+ reloadConfiguration();
+ reloadExecutor.shutdown();
+ }, INITIAL_RELOAD_CONFIG_DELAY, INITIAL_RELOAD_CONFIG_PERIOD, TimeUnit.MILLISECONDS);
+ registry.registerConfigFactory(vplsConfigFactory);
+ }
+
+ @Deactivate
+ void deactivate() {
+ configService.removeListener(configListener);
+ registry.unregisterConfigFactory(vplsConfigFactory);
+ }
+
+ /**
+ * Retrieves the VPLS configuration from network configuration.
+ * Checks difference between new configuration and old configuration.
+ */
+ private synchronized void reloadConfiguration() {
+ VplsAppConfig vplsAppConfig = configService.getConfig(appId, VplsAppConfig.class);
+
+ if (vplsAppConfig == null) {
+ log.warn(CONFIG_NULL);
+
+ // If the config is null, removes all VPLS.
+ vpls.removeAllVpls();
+ return;
+ }
+
+ // If there exists a update time in the configuration; it means the
+ // configuration was pushed by VPLS store; ignore this configuration.
+ long updateTime = vplsAppConfig.updateTime();
+ if (updateTime != -1L) {
+ return;
+ }
+
+ Collection<VplsData> oldVplses = vpls.getAllVpls();
+ Collection<VplsData> newVplses;
+
+ // Generates collection of new VPLSs
+ newVplses = vplsAppConfig.vplss().stream()
+ .map(vplsConfig -> {
+ Set<Interface> interfaces = vplsConfig.ifaces().stream()
+ .map(this::getInterfaceByName)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toSet());
+ VplsData vplsData = VplsData.of(vplsConfig.name(), vplsConfig.encap());
+ vplsData.addInterfaces(interfaces);
+ return vplsData;
+ }).collect(Collectors.toSet());
+
+ if (newVplses.containsAll(oldVplses) && oldVplses.containsAll(newVplses)) {
+ // no update, ignore
+ return;
+ }
+
+ // To add or update
+ newVplses.forEach(newVplsData -> {
+ boolean vplsExists = false;
+ for (VplsData oldVplsData : oldVplses) {
+ if (oldVplsData.name().equals(newVplsData.name())) {
+ vplsExists = true;
+
+ // VPLS exists; but need to be updated.
+ if (!oldVplsData.equals(newVplsData)) {
+ // Update VPLS
+ Set<Interface> newInterfaces = newVplsData.interfaces();
+ Set<Interface> oldInterfaces = oldVplsData.interfaces();
+
+ Set<Interface> ifaceToAdd = newInterfaces.stream()
+ .filter(iface -> !oldInterfaces.contains(iface))
+ .collect(Collectors.toSet());
+
+ Set<Interface> ifaceToRem = oldInterfaces.stream()
+ .filter(iface -> !newInterfaces.contains(iface))
+ .collect(Collectors.toSet());
+
+ vpls.addInterfaces(oldVplsData, ifaceToAdd);
+ vpls.removeInterfaces(oldVplsData, ifaceToRem);
+ vpls.setEncapsulationType(oldVplsData, newVplsData.encapsulationType());
+ }
+ }
+ }
+ // VPLS not exist; add new VPLS
+ if (!vplsExists) {
+ vpls.createVpls(newVplsData.name(), newVplsData.encapsulationType());
+ vpls.addInterfaces(newVplsData, newVplsData.interfaces());
+ }
+ });
+
+ // VPLS not exists in old VPLS configuration; remove it.
+ Set<VplsData> vplsToRemove = Sets.newHashSet();
+ oldVplses.forEach(oldVpls -> {
+ Set<String> newVplsNames = newVplses.stream()
+ .map(VplsData::name)
+ .collect(Collectors.toSet());
+
+ if (!newVplsNames.contains(oldVpls.name())) {
+ // To avoid ConcurrentModificationException; do remove after this
+ // iteration.
+ vplsToRemove.add(oldVpls);
+ }
+ });
+ vplsToRemove.forEach(vpls::removeVpls);
+ }
+
+ /**
+ * Gets network interface by a given name.
+ *
+ * @param interfaceName the interface name
+ * @return the network interface if there exist with the given name; null
+ * otherwise
+ */
+ private Interface getInterfaceByName(String interfaceName) {
+ return interfaceService.getInterfaces().stream()
+ .filter(iface -> iface.name().equals(interfaceName))
+ .findFirst()
+ .orElse(null);
+ }
+
+ /**
+ * Listener for VPLS configuration events.
+ * Reloads VPLS configuration when configuration added or updated.
+ * Removes all VPLS when configuration removed or unregistered.
+ */
+ private class InternalNetworkConfigListener implements NetworkConfigListener {
+ @Override
+ public void event(NetworkConfigEvent event) {
+ if (event.configClass() == CONFIG_CLASS) {
+ log.debug(NET_CONF_EVENT, event.configClass());
+ switch (event.type()) {
+ case CONFIG_ADDED:
+ case CONFIG_UPDATED:
+ reloadConfiguration();
+ break;
+ case CONFIG_REMOVED:
+ case CONFIG_UNREGISTERED:
+ vpls.removeAllVpls();
+ default:
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/config/VplsConfigService.java b/apps/vpls/src/main/java/org/onosproject/vpls/config/VplsConfigService.java
deleted file mode 100644
index 2d2a576..0000000
--- a/apps/vpls/src/main/java/org/onosproject/vpls/config/VplsConfigService.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Copyright 2016-present 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.vpls.config;
-
-import com.google.common.collect.SetMultimap;
-import org.onlab.packet.VlanId;
-import org.onosproject.incubator.net.intf.Interface;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.EncapsulationType;
-
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Provides information about the VPLS configuration.
- */
-public interface VplsConfigService {
- Class<VplsAppConfig> CONFIG_CLASS = VplsAppConfig.class;
-
- /**
- * Adds a VPLS to the configuration.
- *
- * @param vplsName the name of the VPLS
- * @param ifaces the interfaces associated with the VPLS
- * @param encap the encapsulation type
- */
- void addVpls(String vplsName, Set<String> ifaces, String encap);
-
- /**
- * Removes a VPLS from the configuration.
- *
- * @param vplsName the name of the VPLS to be removed
- */
- void removeVpls(String vplsName);
-
- /**
- * Adds a network interface to a VPLS.
- *
- * @param vplsName the name of the VPLS
- * @param iface the network interface to be added to the VPLS
- */
- void addIface(String vplsName, String iface);
-
- /**
- * Sets an encapsulation parameter for a VPLS.
- *
- * @param vplsName the name of the VPLS
- * @param encap the encapsulation used (i.e. MPLS or VLAN) or
- */
- void setEncap(String vplsName, String encap);
-
- /**
- * Returns the encapsulation type in use for a given VPLS.
- *
- * @param vplsName the name of the VPLS
- * @return the encapsulation type in use, if any
- */
- EncapsulationType encap(String vplsName);
-
- /**
- * Removes a network interface from a VPLS.
- *
- * @param iface the network interface to be removed from the VPLS
- */
- void removeIface(String iface);
-
- /**
- * Cleans up the VPLS configuration. Removes all VPLSs.
- */
- void cleanVplsConfig();
-
- /**
- * Retrieves the VPLS names modified from CLI.
- *
- * @return the VPLS names modified from CLI
- */
- Set<String> vplsAffectedByApi();
- // TODO Remove this function after the intent framework race condition has been fixed
-
- /**
- * Retrieves the interfaces without IP configured, so compatible with VPLS,
- * from the interface service.
- *
- * @return the set of interfaces configured, compatible with VPLS
- */
- Set<Interface> allIfaces();
-
- /**
- * Retrieves the interfaces associated to a VPLS.
- *
- * @return the set of interfaces associated to a VPLS
- */
- Set<Interface> ifaces();
-
- /**
- * Retrieves the interfaces associated to the VPLS specified.
- *
- * @param vplsName the name of the VPLS
- * @return the set of interfaces associated to the VPLS specified
- */
- Set<Interface> ifaces(String vplsName);
-
- /**
- * Retrieves all VPLS names.
- *
- * @return a set of VPLS names
- */
- Set<String> vplsNames();
-
- /**
- * Retrieves all VPLS names from the old config.
- *
- * @return a set of VPLS names
- */
- Set<String> vplsNamesOld();
- // TODO Removes this function after the race condition gets fixed in IF
-
- /**
- * Returns the VPLS names and associated interfaces from the configuration.
- *
- * @return a map of VPLS names and associated interfaces
- */
- SetMultimap<String, Interface> ifacesByVplsName();
-
- /**
- * Returns the list of interfaces grouped by VPLS name, given a VLAN Id and
- * a connect point.
- *
- * @param vlan the VLAN Id
- * @param connectPoint the connect point
- * @return a map of VPLS names and associated interfaces; null otherwise
- */
- SetMultimap<String, Interface> ifacesByVplsName(VlanId vlan,
- ConnectPoint connectPoint);
-
- /**
- * Returns the VPLS names and associated encapsulation type.
- *
- * @return a map of VPLS names and associated encapsulation type
- */
- Map<String, EncapsulationType> encapByVplsName();
-}
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/config/impl/VplsConfigImpl.java b/apps/vpls/src/main/java/org/onosproject/vpls/config/impl/VplsConfigImpl.java
deleted file mode 100644
index 36da63c..0000000
--- a/apps/vpls/src/main/java/org/onosproject/vpls/config/impl/VplsConfigImpl.java
+++ /dev/null
@@ -1,388 +0,0 @@
-/*
- * Copyright 2016-present 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.vpls.config.impl;
-
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSetMultimap;
-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.Service;
-import org.onlab.packet.VlanId;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.core.CoreService;
-import org.onosproject.incubator.net.intf.Interface;
-import org.onosproject.incubator.net.intf.InterfaceService;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.EncapsulationType;
-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.basics.SubjectFactories;
-import org.onosproject.vpls.config.VplsAppConfig;
-import org.onosproject.vpls.config.VplsConfig;
-import org.onosproject.vpls.config.VplsConfigService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Implementation of VPLSConfigurationService which reads VPLS configuration
- * from the network configuration service.
- */
-@Component(immediate = true)
-@Service
-public class VplsConfigImpl implements VplsConfigService {
- private static final String VPLS_APP = "org.onosproject.vpls";
- private static final String VPLS = "vpls";
- private static final String EMPTY = "";
- private static final String CONFIG_NULL = "VPLS configuration not defined";
- private static final String APP_ID_NULL = "VPLS application ID is null";
- private static final String CONFIG_CHANGED = "VPLS configuration changed: {}";
- private static final String CHECK_CONFIG =
- "Checking the interface configuration";
- private static final String NET_CONF_EVENT =
- "Received NetworkConfigEvent {}";
-
- private final Logger log = LoggerFactory.getLogger(getClass());
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected NetworkConfigRegistry registry;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected CoreService coreService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected InterfaceService interfaceService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected NetworkConfigService configService;
-
- private final Set<String> vplsAffectedByApi = new HashSet<>();
-
- private VplsAppConfig vplsAppConfig = null;
-
- private SetMultimap<String, String> ifacesOfVpls = HashMultimap.create();
- private SetMultimap<String, String> oldIfacesOfVpls = HashMultimap.create();
- private SetMultimap<String, Interface> vplsIfaces = HashMultimap.create();
-
- private Map<String, EncapsulationType> vplsEncaps = Maps.newHashMap();
-
- private final InternalNetworkConfigListener configListener =
- new InternalNetworkConfigListener();
-
- private ConfigFactory<ApplicationId, VplsAppConfig> vplsConfigFactory =
- new ConfigFactory<ApplicationId, VplsAppConfig>(
- SubjectFactories.APP_SUBJECT_FACTORY, VplsAppConfig.class, VPLS) {
- @Override
- public VplsAppConfig createConfig() {
- return new VplsAppConfig();
- }
- };
-
- private ApplicationId vplsAppId;
-
- @Activate
- protected void active() {
- configService.addListener(configListener);
- registry.registerConfigFactory(vplsConfigFactory);
- loadConfiguration();
- log.info("Started");
- }
-
- @Deactivate
- protected void deactive() {
- registry.unregisterConfigFactory(vplsConfigFactory);
- configService.removeListener(configListener);
- log.info("Stopped");
- }
-
- @Override
- public void addVpls(String vplsName, Set<String> ifaces, String encap) {
- EncapsulationType encapType = EncapsulationType.enumFromString(encap);
-
- if (ifacesOfVpls.containsKey(vplsName)) {
- if (ifaces.isEmpty()) {
- return;
- }
- ifaces.forEach(iface -> vplsAppConfig.addIface(vplsName, iface));
- vplsAppConfig.setEncap(vplsName, encapType);
- } else {
- vplsAppConfig.addVpls(new VplsConfig(vplsName, ifaces, encapType));
- }
-
- vplsAffectedByApi.add(vplsName);
- applyConfig(vplsAppConfig);
- }
-
- @Override
- public void removeVpls(String vplsName) {
- if (ifacesOfVpls.containsKey(vplsName)) {
- vplsAppConfig.removeVpls(vplsName);
- vplsAffectedByApi.add(vplsName);
- applyConfig(vplsAppConfig);
- }
- }
-
- @Override
- public void addIface(String vplsName, String iface) {
- if (ifacesOfVpls.containsKey(vplsName)) {
- vplsAppConfig.addIface(vplsName, iface);
- vplsAffectedByApi.add(vplsName);
- applyConfig(vplsAppConfig);
- }
- }
-
- @Override
- public void setEncap(String vplsName, String encap) {
- EncapsulationType encapType = EncapsulationType.enumFromString(encap);
-
- if (ifacesOfVpls.containsKey(vplsName)) {
- vplsAppConfig.setEncap(vplsName, encapType);
- vplsAffectedByApi.add(vplsName);
- applyConfig(vplsAppConfig);
- }
- }
-
- @Override
- public void removeIface(String iface) {
- if (ifacesOfVpls.containsValue(iface)) {
- VplsConfig vpls = vplsAppConfig.vplsFromIface(iface);
- vplsAppConfig.removeIface(vpls, iface);
- vplsAffectedByApi.add(vpls.name());
- applyConfig(vplsAppConfig);
- }
- }
-
- @Override
- public void cleanVplsConfig() {
- ifacesOfVpls.entries().forEach(e -> {
- vplsAppConfig.removeVpls(e.getKey());
- vplsAffectedByApi.add(e.getKey());
- });
- applyConfig(vplsAppConfig);
- }
-
- @Override
- public EncapsulationType encap(String vplsName) {
- EncapsulationType encap = null;
- if (vplsEncaps.containsKey(vplsName)) {
- encap = vplsEncaps.get(vplsName);
- }
-
- return encap;
- }
-
- @Override
- public Set<String> vplsAffectedByApi() {
- Set<String> vplsNames = ImmutableSet.copyOf(vplsAffectedByApi);
- vplsAffectedByApi.clear();
- return vplsNames;
- }
-
- @Override
- public Set<Interface> allIfaces() {
- Set<Interface> interfaces = new HashSet<>();
- interfaceService.getInterfaces().stream()
- .filter(iface -> iface.ipAddressesList() == null ||
- iface.ipAddressesList().isEmpty())
- .forEach(interfaces::add);
- return interfaces;
- }
-
- @Override
- public Set<Interface> ifaces() {
- Set<Interface> interfaces = new HashSet<>();
- vplsIfaces.values().forEach(interfaces::add);
- return interfaces;
- }
-
- @Override
- public Set<Interface> ifaces(String vplsName) {
- Set<Interface> vplsInterfaces = new HashSet<>();
- vplsIfaces.get(vplsName).forEach(vplsInterfaces::add);
- return vplsInterfaces;
- }
-
- @Override
- public Set<String> vplsNames() {
- return ifacesOfVpls.keySet();
- }
-
- @Override
- public Set<String> vplsNamesOld() {
- return oldIfacesOfVpls.keySet();
- }
-
- @Override
- public SetMultimap<String, Interface> ifacesByVplsName() {
- return ImmutableSetMultimap.copyOf(vplsIfaces);
- }
-
- @Override
- public SetMultimap<String, Interface> ifacesByVplsName(VlanId vlan,
- ConnectPoint connectPoint) {
- String vplsName =
- vplsIfaces.entries().stream()
- .filter(e -> e.getValue().connectPoint().equals(connectPoint))
- .filter(e -> e.getValue().vlan().equals(vlan))
- .map(Map.Entry::getKey)
- .findFirst()
- .orElse(null);
- SetMultimap<String, Interface> result = HashMultimap.create();
- if (vplsName != null && vplsIfaces.containsKey(vplsName)) {
- vplsIfaces.get(vplsName)
- .forEach(intf -> result.put(vplsName, intf));
- return result;
- }
- return null;
- }
-
- @Override
- public Map<String, EncapsulationType> encapByVplsName() {
- return ImmutableMap.copyOf(vplsEncaps);
- }
-
- /**
- * Retrieves the VPLS configuration from network configuration.
- */
- private void loadConfiguration() {
- loadAppId();
-
- vplsAppConfig = configService.getConfig(vplsAppId, VplsAppConfig.class);
-
- if (vplsAppConfig == null) {
- log.warn(CONFIG_NULL);
- vplsAppConfig = configService.addConfig(vplsAppId, VplsAppConfig.class);
- }
-
- oldIfacesOfVpls = ifacesOfVpls;
- ifacesOfVpls = getConfigInterfaces();
- vplsIfaces = getConfigCPointsFromIfaces();
- vplsEncaps = getConfigEncap();
-
- log.debug(CONFIG_CHANGED, ifacesOfVpls);
- }
-
- /**
- * Retrieves the application identifier from core service.
- */
- private void loadAppId() {
- vplsAppId = coreService.getAppId(VPLS_APP);
- if (vplsAppId == null) {
- log.warn(APP_ID_NULL);
- }
- }
-
- /**
- * Applies a given configuration to the VPLS application.
- */
- private void applyConfig(VplsAppConfig vplsAppConfig) {
- loadAppId();
- configService.applyConfig(vplsAppId, VplsAppConfig.class, vplsAppConfig.node());
- }
-
- /**
- * Retrieves the VPLS names and related encapsulation types from the
- * configuration.
- *
- * @return a map of VPLS names and associated encapsulation types
- */
- private Map<String, EncapsulationType> getConfigEncap() {
- Map<String, EncapsulationType> configEncap = new HashMap<>();
-
- vplsAppConfig.vplss().forEach(vpls -> {
- configEncap.put(vpls.name(), vpls.encap());
- });
-
- return configEncap;
- }
-
- /**
- * Retrieves the VPLS names and related interfaces names from the configuration.
- *
- * @return a map of VPLS names and related interface names
- */
- private SetMultimap<String, String> getConfigInterfaces() {
- SetMultimap<String, String> confIntfByVpls =
- HashMultimap.create();
-
- vplsAppConfig.vplss().forEach(vpls -> {
- if (vpls.ifaces().isEmpty()) {
- confIntfByVpls.put(vpls.name(), EMPTY);
- } else {
- vpls.ifaces().forEach(iface -> confIntfByVpls.put(vpls.name(), iface));
- }
- });
-
- return confIntfByVpls;
- }
-
- /**
- * Retrieves the VPLS names and related interfaces from the configuration.
- *
- * @return a map of VPLS names and related interfaces
- */
- private SetMultimap<String, Interface> getConfigCPointsFromIfaces() {
- log.debug(CHECK_CONFIG);
-
- SetMultimap<String, Interface> confCPointsByIntf =
- HashMultimap.create();
-
- ifacesOfVpls.entries().forEach(vpls -> {
- interfaceService.getInterfaces()
- .stream()
- .filter(intf -> intf.ipAddressesList().isEmpty())
- .filter(intf -> intf.name().equals(vpls.getValue()))
- .forEach(intf -> confCPointsByIntf.put(vpls.getKey(), intf));
- });
-
- return confCPointsByIntf;
- }
-
- /**
- * Listener for VPLS configuration events.
- */
- private class InternalNetworkConfigListener implements NetworkConfigListener {
- @Override
- public void event(NetworkConfigEvent event) {
- if (event.configClass() == VplsConfigService.CONFIG_CLASS) {
- log.debug(NET_CONF_EVENT, event.configClass());
- switch (event.type()) {
- case CONFIG_ADDED:
- case CONFIG_UPDATED:
- case CONFIG_REMOVED:
- loadConfiguration();
- break;
- default:
- break;
- }
- }
- }
- }
-}
\ No newline at end of file
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/intent/VplsIntentUtility.java b/apps/vpls/src/main/java/org/onosproject/vpls/intent/VplsIntentUtility.java
new file mode 100644
index 0000000..a81a72f
--- /dev/null
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/intent/VplsIntentUtility.java
@@ -0,0 +1,339 @@
+/*
+ * Copyright 2017-present 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.vpls.intent;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Sets;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.EncapsulationType;
+import org.onosproject.net.FilteredConnectPoint;
+import org.onosproject.net.Host;
+import org.onosproject.net.ResourceGroup;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.intent.ConnectivityIntent;
+import org.onosproject.net.intent.Constraint;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.Key;
+import org.onosproject.net.intent.MultiPointToSinglePointIntent;
+import org.onosproject.net.intent.SinglePointToMultiPointIntent;
+import org.onosproject.net.intent.constraint.EncapsulationConstraint;
+import org.onosproject.net.intent.constraint.PartialFailureConstraint;
+import org.onosproject.vpls.api.VplsData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static java.util.Objects.*;
+import static org.onosproject.net.EncapsulationType.*;
+
+/**
+ * Intent utilities for VPLS.
+ */
+public final class VplsIntentUtility {
+ private static final String SP2MP =
+ "Building sp2mp intent from {}";
+ private static final String MP2SP =
+ "Building mp2sp intent to {}";
+
+ private static final Logger log = LoggerFactory.getLogger(
+ VplsIntentUtility.class);
+
+ private static final int PRIORITY_OFFSET = 1000;
+ private static final int PRIORITY_UNI = 200;
+ private static final int PRIORITY_BRC = 100;
+
+ public static final String PREFIX_BROADCAST = "brc";
+ public static final String PREFIX_UNICAST = "uni";
+ private static final String SEPARATOR = "-";
+
+ public static final ImmutableList<Constraint> PARTIAL_FAILURE_CONSTRAINT =
+ ImmutableList.of(new PartialFailureConstraint());
+
+ private VplsIntentUtility() {
+ // Utility classes should not have a public or default constructor.
+ }
+
+ /**
+ * Builds broadcast Intents for a VPLS.
+ *
+ * @param vplsData the VPLS
+ * @param appId the application id for Intents
+ * @return broadcast Intents for the VPLS
+ */
+ public static Set<Intent> buildBrcIntents(VplsData vplsData, ApplicationId appId) {
+ Set<Interface> interfaces = vplsData.interfaces();
+
+ // At least two or more network interfaces to build broadcast Intents
+ if (interfaces.size() < 2) {
+ return ImmutableSet.of();
+ }
+ Set<Intent> brcIntents = Sets.newHashSet();
+ ResourceGroup resourceGroup = ResourceGroup.of(vplsData.name());
+
+ // Generates broadcast Intents from any network interface to other
+ // network interface from the VPLS.
+ interfaces.forEach(src -> {
+ FilteredConnectPoint srcFcp = VplsIntentUtility.buildFilteredConnectedPoint(src);
+ Set<FilteredConnectPoint> dstFcps =
+ interfaces.stream()
+ .filter(iface -> !iface.equals(src))
+ .map(VplsIntentUtility::buildFilteredConnectedPoint)
+ .collect(Collectors.toSet());
+ Key key = VplsIntentUtility.buildKey(PREFIX_BROADCAST,
+ srcFcp.connectPoint(),
+ vplsData.name(),
+ MacAddress.BROADCAST,
+ appId);
+ Intent brcIntent = buildBrcIntent(key,
+ appId,
+ srcFcp,
+ dstFcps,
+ vplsData.encapsulationType(),
+ resourceGroup);
+
+ brcIntents.add(brcIntent);
+ });
+ return brcIntents;
+ }
+
+ /**
+ * Builds a broadcast intent.
+ *
+ * @param key key to identify the intent
+ * @param appId application ID for this Intent
+ * @param src the source connect point
+ * @param dsts the destination connect points
+ * @param encap the encapsulation type
+ * @param resourceGroup resource group for this Intent
+ * @return the generated single-point to multi-point intent
+ */
+ protected static SinglePointToMultiPointIntent buildBrcIntent(Key key,
+ ApplicationId appId,
+ FilteredConnectPoint src,
+ Set<FilteredConnectPoint> dsts,
+ EncapsulationType encap,
+ ResourceGroup resourceGroup) {
+ log.debug("Building broadcast intent {} for source {}", SP2MP, src);
+
+ SinglePointToMultiPointIntent.Builder intentBuilder;
+
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchEthDst(MacAddress.BROADCAST)
+ .build();
+
+ intentBuilder = SinglePointToMultiPointIntent.builder()
+ .appId(appId)
+ .key(key)
+ .selector(selector)
+ .filteredIngressPoint(src)
+ .filteredEgressPoints(dsts)
+ .constraints(PARTIAL_FAILURE_CONSTRAINT)
+ .priority(PRIORITY_OFFSET + PRIORITY_BRC)
+ .resourceGroup(resourceGroup);
+
+ setEncap(intentBuilder, PARTIAL_FAILURE_CONSTRAINT, encap);
+
+ return intentBuilder.build();
+ }
+
+ /**
+ * Builds unicast Intents for a VPLS.
+ *
+ * @param vplsData the VPLS
+ * @param hosts the hosts of the VPLS
+ * @param appId application ID for Intents
+ * @return unicast Intents for the VPLS
+ */
+ public static Set<Intent> buildUniIntents(VplsData vplsData, Set<Host> hosts, ApplicationId appId) {
+ Set<Interface> interfaces = vplsData.interfaces();
+ if (interfaces.size() < 2) {
+ return ImmutableSet.of();
+ }
+ Set<Intent> uniIntents = Sets.newHashSet();
+ ResourceGroup resourceGroup = ResourceGroup.of(vplsData.name());
+ hosts.forEach(host -> {
+ FilteredConnectPoint hostFcp = buildFilteredConnectedPoint(host);
+ Set<FilteredConnectPoint> srcFcps =
+ interfaces.stream()
+ .map(VplsIntentUtility::buildFilteredConnectedPoint)
+ .filter(fcp -> !fcp.equals(hostFcp))
+ .collect(Collectors.toSet());
+ Key key = buildKey(PREFIX_UNICAST,
+ hostFcp.connectPoint(),
+ vplsData.name(),
+ host.mac(),
+ appId);
+ Intent uniIntent = buildUniIntent(key,
+ appId,
+ srcFcps,
+ hostFcp,
+ host,
+ vplsData.encapsulationType(),
+ resourceGroup);
+ uniIntents.add(uniIntent);
+ });
+
+ return uniIntents;
+ }
+
+ /**
+ * Builds a unicast intent.
+ *
+ * @param key key to identify the intent
+ * @param appId application ID for this Intent
+ * @param srcs the source Connect Points
+ * @param dst the destination Connect Point
+ * @param host destination Host
+ * @param encap the encapsulation type
+ * @param resourceGroup resource group for this Intent
+ * @return the generated multi-point to single-point intent
+ */
+ protected static MultiPointToSinglePointIntent buildUniIntent(Key key,
+ ApplicationId appId,
+ Set<FilteredConnectPoint> srcs,
+ FilteredConnectPoint dst,
+ Host host,
+ EncapsulationType encap,
+ ResourceGroup resourceGroup) {
+ log.debug("Building unicast intent {} for destination {}", MP2SP, dst);
+
+ MultiPointToSinglePointIntent.Builder intentBuilder;
+
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchEthDst(host.mac())
+ .build();
+
+ intentBuilder = MultiPointToSinglePointIntent.builder()
+ .appId(appId)
+ .key(key)
+ .selector(selector)
+ .filteredIngressPoints(srcs)
+ .filteredEgressPoint(dst)
+ .constraints(PARTIAL_FAILURE_CONSTRAINT)
+ .priority(PRIORITY_OFFSET + PRIORITY_UNI)
+ .resourceGroup(resourceGroup);
+
+ setEncap(intentBuilder, PARTIAL_FAILURE_CONSTRAINT, encap);
+
+ return intentBuilder.build();
+ }
+
+ /**
+ * Builds an intent key either for single-point to multi-point or
+ * multi-point to single-point intents, based on a prefix that defines
+ * the type of intent, the single connect point representing the single
+ * source or destination for that intent, the name of the VPLS the intent
+ * belongs to, and the destination host MAC address the intent reaches.
+ *
+ * @param prefix the key prefix
+ * @param cPoint the connect point identifying the source/destination
+ * @param vplsName the name of the VPLS
+ * @param hostMac the source/destination MAC address
+ * @param appId application ID for the key
+ * @return the key to identify the intent
+ */
+ protected static Key buildKey(String prefix,
+ ConnectPoint cPoint,
+ String vplsName,
+ MacAddress hostMac,
+ ApplicationId appId) {
+ String keyString = vplsName +
+ SEPARATOR +
+ prefix +
+ SEPARATOR +
+ cPoint.deviceId() +
+ SEPARATOR +
+ cPoint.port() +
+ SEPARATOR +
+ hostMac;
+
+ return Key.of(keyString, appId);
+ }
+
+
+ /**
+ * Sets one or more encapsulation constraints on the intent builder given.
+ *
+ * @param builder the intent builder
+ * @param constraints the existing intent constraints
+ * @param encap the encapsulation type to be set
+ */
+ public static void setEncap(ConnectivityIntent.Builder builder,
+ List<Constraint> constraints,
+ EncapsulationType encap) {
+ // Constraints might be an immutable list, so a new modifiable list
+ // is created
+ List<Constraint> newConstraints = new ArrayList<>(constraints);
+
+ // Remove any encapsulation constraint if already in the list
+ constraints.stream()
+ .filter(c -> c instanceof EncapsulationConstraint)
+ .forEach(newConstraints::remove);
+
+ // if the new encapsulation is different from NONE, a new encapsulation
+ // constraint should be added to the list
+ if (!encap.equals(NONE)) {
+ newConstraints.add(new EncapsulationConstraint(encap));
+ }
+
+ // Submit new constraint list as immutable list
+ builder.constraints(ImmutableList.copyOf(newConstraints));
+ }
+
+ /**
+ * Builds filtered connected point by a given network interface.
+ *
+ * @param iface the network interface
+ * @return the filtered connected point of a given network interface
+ */
+ protected static FilteredConnectPoint buildFilteredConnectedPoint(Interface iface) {
+ Objects.requireNonNull(iface);
+ TrafficSelector.Builder trafficSelector = DefaultTrafficSelector.builder();
+
+ if (iface.vlan() != null && !iface.vlan().equals(VlanId.NONE)) {
+ trafficSelector.matchVlanId(iface.vlan());
+ }
+
+ return new FilteredConnectPoint(iface.connectPoint(), trafficSelector.build());
+ }
+
+ /**
+ * Builds filtered connected point by a given host.
+ *
+ * @param host the host
+ * @return the filtered connected point of the given host
+ */
+ protected static FilteredConnectPoint buildFilteredConnectedPoint(Host host) {
+ requireNonNull(host);
+ TrafficSelector.Builder trafficSelector = DefaultTrafficSelector.builder();
+
+ if (host.vlan() != null && !host.vlan().equals(VlanId.NONE)) {
+ trafficSelector.matchVlanId(host.vlan());
+ }
+ return new FilteredConnectPoint(host.location(), trafficSelector.build());
+ }
+}
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/config/impl/package-info.java b/apps/vpls/src/main/java/org/onosproject/vpls/intent/package-info.java
similarity index 79%
copy from apps/vpls/src/main/java/org/onosproject/vpls/config/impl/package-info.java
copy to apps/vpls/src/main/java/org/onosproject/vpls/intent/package-info.java
index b5d6ec9..197ea02 100644
--- a/apps/vpls/src/main/java/org/onosproject/vpls/config/impl/package-info.java
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/intent/package-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-present Open Networking Laboratory
+ * Copyright 2017-present 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.
@@ -15,6 +15,6 @@
*/
/**
- * Configuration service implementation for VPLS.
+ * Utility for generate Intent for VPLS.
*/
-package org.onosproject.vpls.config.impl;
+package org.onosproject.vpls.intent;
\ No newline at end of file
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/store/DistributedVplsStore.java b/apps/vpls/src/main/java/org/onosproject/vpls/store/DistributedVplsStore.java
new file mode 100644
index 0000000..1743491
--- /dev/null
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/store/DistributedVplsStore.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2017-present 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.vpls.store;
+
+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.KryoNamespace;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.net.config.NetworkConfigService;
+import org.onosproject.store.AbstractStore;
+import org.onosproject.store.StoreDelegate;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.EventuallyConsistentMap;
+import org.onosproject.store.service.EventuallyConsistentMapEvent;
+import org.onosproject.store.service.EventuallyConsistentMapListener;
+import org.onosproject.store.service.StorageService;
+import org.onosproject.store.service.WallClockTimestamp;
+import org.onosproject.vpls.VplsManager;
+import org.onosproject.vpls.api.VplsData;
+import org.onosproject.vpls.api.VplsOperation;
+import org.onosproject.vpls.config.VplsAppConfig;
+import org.onosproject.vpls.api.VplsStore;
+import org.onosproject.vpls.config.VplsConfig;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Collectors;
+
+import static java.util.Objects.*;
+
+/**
+ * Implementation of VPLSConfigurationService which reads VPLS configuration
+ * from the network configuration service.
+ */
+@Component(immediate = true)
+@Service
+public class DistributedVplsStore
+ extends AbstractStore<VplsStoreEvent, StoreDelegate<VplsStoreEvent>>
+ implements VplsStore {
+
+ private static final KryoNamespace APP_KRYO = KryoNamespace.newBuilder()
+ .register(KryoNamespaces.API)
+ .register(VplsData.class)
+ .register(VplsOperation.class)
+ .build();
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected StorageService storageService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected NetworkConfigService networkConfigService;
+
+ private EventuallyConsistentMap<String, VplsData> vplsDataStore;
+ private EventuallyConsistentMapListener<String, VplsData> vplsDataListener;
+ private ApplicationId appId;
+
+ @Activate
+ protected void active() {
+ appId = coreService.registerApplication(VplsManager.VPLS_APP);
+
+ vplsDataStore = storageService.<String, VplsData>eventuallyConsistentMapBuilder()
+ .withName("VPLS-Data")
+ .withTimestampProvider((name, vpls) -> new WallClockTimestamp())
+ .withSerializer(APP_KRYO)
+ .build();
+ vplsDataListener = new InternalVplsDataListener();
+ vplsDataStore.addListener(vplsDataListener);
+ log.info("Started");
+ }
+
+ @Deactivate
+ protected void deactive() {
+ vplsDataStore.removeListener(vplsDataListener);
+ log.info("Stopped");
+ }
+
+ @Override
+ public void addVpls(VplsData vplsData) {
+ requireNonNull(vplsData);
+ if (vplsData.name().isEmpty()) {
+ throw new IllegalArgumentException("VPLS name is empty.");
+ }
+ vplsData.state(VplsData.VplsState.ADDING);
+ this.vplsDataStore.put(vplsData.name(), vplsData);
+ }
+
+ @Override
+ public void removeVpls(VplsData vplsData) {
+ requireNonNull(vplsData);
+ if (vplsData.name().isEmpty()) {
+ throw new IllegalArgumentException("VPLS name is empty.");
+ }
+ vplsData.state(VplsData.VplsState.REMOVING);
+ if (!this.vplsDataStore.containsKey(vplsData.name())) {
+ // notify the delegate asynchronously if VPLS does not exists
+ CompletableFuture.runAsync(() -> {
+ VplsStoreEvent event = new VplsStoreEvent(VplsStoreEvent.Type.REMOVE,
+ vplsData);
+ notifyDelegate(event);
+ });
+ return;
+ }
+ this.vplsDataStore.remove(vplsData.name());
+ }
+
+ @Override
+ public void updateVpls(VplsData vplsData) {
+ switch (vplsData.state()) {
+ case ADDED:
+ case REMOVED:
+ case FAILED:
+ // state update only
+ this.vplsDataStore.put(vplsData.name(), vplsData);
+ break;
+ default:
+ vplsData.state(VplsData.VplsState.UPDATING);
+ this.vplsDataStore.put(vplsData.name(), vplsData);
+ break;
+ }
+ }
+
+ @Override
+ public VplsData getVpls(String vplsName) {
+ return vplsDataStore.get(vplsName);
+ }
+
+ @Override
+ public Collection<VplsData> getAllVpls() {
+ return vplsDataStore.values();
+ }
+
+ /**
+ * Writes all VPLS data to the network configuration store.
+ *
+ * @param vplsDataCollection the VPLSs data
+ */
+ public void writeVplsToNetConfig(Collection<VplsData> vplsDataCollection) {
+ VplsAppConfig config = networkConfigService.addConfig(appId, VplsAppConfig.class);
+ config.clearVplsConfig();
+
+ // Setup update time for this VPLS application configuration
+ WallClockTimestamp ts = new WallClockTimestamp();
+ config.updateTime(ts.unixTimestamp());
+
+ vplsDataCollection.forEach(vplsData -> {
+ Set<String> interfaceNames = vplsData.interfaces()
+ .stream()
+ .map(Interface::name)
+ .collect(Collectors.toSet());
+ VplsConfig vplsConfig = new VplsConfig(vplsData.name(), interfaceNames,
+ vplsData.encapsulationType());
+ config.addVpls(vplsConfig);
+ });
+
+ networkConfigService.applyConfig(appId, VplsAppConfig.class, config.node());
+ }
+
+ /**
+ * Listener for VPLS data store.
+ */
+ private class InternalVplsDataListener implements EventuallyConsistentMapListener<String, VplsData> {
+ private static final String STATE_UPDATE = "VPLS state updated, new VPLS: {}";
+
+ @Override
+ public void event(EventuallyConsistentMapEvent<String, VplsData> event) {
+ VplsData vplsData = event.value();
+ // Update network config
+ writeVplsToNetConfig(getAllVpls());
+ switch (event.type()) {
+ case PUT:
+ // Add or Update
+ if (vplsData.state() == VplsData.VplsState.ADDING) {
+ VplsStoreEvent vplsStoreEvent =
+ new VplsStoreEvent(VplsStoreEvent.Type.ADD, vplsData);
+ notifyDelegate(vplsStoreEvent);
+ } else if (vplsData.state() == VplsData.VplsState.UPDATING) {
+ VplsStoreEvent vplsStoreEvent =
+ new VplsStoreEvent(VplsStoreEvent.Type.UPDATE, vplsData);
+ notifyDelegate(vplsStoreEvent);
+ } else {
+ // Do nothing here, just update state from operation service
+ log.debug(STATE_UPDATE, vplsData);
+ }
+ break;
+ case REMOVE:
+ VplsStoreEvent vplsStoreEvent =
+ new VplsStoreEvent(VplsStoreEvent.Type.REMOVE, vplsData);
+ notifyDelegate(vplsStoreEvent);
+ break;
+ default:
+ break;
+ }
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/store/VplsStoreEvent.java b/apps/vpls/src/main/java/org/onosproject/vpls/store/VplsStoreEvent.java
new file mode 100644
index 0000000..210470c
--- /dev/null
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/store/VplsStoreEvent.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2017-present 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.vpls.store;
+
+import org.onosproject.event.AbstractEvent;
+import org.onosproject.vpls.api.VplsData;
+
+/**
+ * A class to represent a VPLS store related event.
+ */
+public class VplsStoreEvent extends AbstractEvent<VplsStoreEvent.Type, VplsData> {
+
+ /**
+ * VPLS store event type.
+ */
+ public enum Type {
+ ADD,
+ REMOVE,
+ UPDATE
+ }
+
+ /**
+ * Constructs a store event with given event type and VPLS information.
+ *
+ * @param type the event type
+ * @param subject the VPLS
+ */
+ public VplsStoreEvent(Type type, VplsData subject) {
+ super(type, subject);
+ }
+
+}
diff --git a/apps/vpls/src/main/java/org/onosproject/vpls/config/impl/package-info.java b/apps/vpls/src/main/java/org/onosproject/vpls/store/package-info.java
similarity index 78%
copy from apps/vpls/src/main/java/org/onosproject/vpls/config/impl/package-info.java
copy to apps/vpls/src/main/java/org/onosproject/vpls/store/package-info.java
index b5d6ec9..0f1a807 100644
--- a/apps/vpls/src/main/java/org/onosproject/vpls/config/impl/package-info.java
+++ b/apps/vpls/src/main/java/org/onosproject/vpls/store/package-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-present Open Networking Laboratory
+ * Copyright 2017-present 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.
@@ -14,7 +14,4 @@
* limitations under the License.
*/
-/**
- * Configuration service implementation for VPLS.
- */
-package org.onosproject.vpls.config.impl;
+package org.onosproject.vpls.store;
\ No newline at end of file