| /* |
| * Copyright 2017-present Open Networking Foundation |
| * |
| * 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.net.intf.Interface; |
| import org.onosproject.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(); |
| break; |
| default: |
| break; |
| } |
| } |
| } |
| } |
| } |