blob: 14a755e391c3c9c3e41fdca54a03704a4b6798db [file] [log] [blame]
Yi Tsengf4e13e32017-03-30 15:38:39 -07001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2017-present Open Networking Foundation
Yi Tsengf4e13e32017-03-30 15:38:39 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.onosproject.vpls.config;
18
19import com.google.common.collect.Sets;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070020import org.osgi.service.component.annotations.Activate;
21import org.osgi.service.component.annotations.Component;
22import org.osgi.service.component.annotations.Deactivate;
23import org.osgi.service.component.annotations.Reference;
24import org.osgi.service.component.annotations.ReferenceCardinality;
Yi Tsengf4e13e32017-03-30 15:38:39 -070025import org.onosproject.cluster.LeadershipService;
26import org.onosproject.cluster.NodeId;
27import org.onosproject.core.ApplicationId;
28import org.onosproject.core.CoreService;
Ray Milkeyfacf2862017-08-03 11:58:29 -070029import org.onosproject.net.intf.Interface;
30import org.onosproject.net.intf.InterfaceService;
Yi Tsengf4e13e32017-03-30 15:38:39 -070031import org.onosproject.net.config.ConfigFactory;
32import org.onosproject.net.config.NetworkConfigEvent;
33import org.onosproject.net.config.NetworkConfigListener;
34import org.onosproject.net.config.NetworkConfigRegistry;
35import org.onosproject.net.config.NetworkConfigService;
36import org.onosproject.net.config.basics.SubjectFactories;
37import org.onosproject.vpls.VplsManager;
38import org.onosproject.vpls.api.VplsData;
39import org.onosproject.vpls.api.Vpls;
40import org.slf4j.Logger;
41import org.slf4j.LoggerFactory;
42
43import java.util.Collection;
44import java.util.Objects;
45import java.util.Set;
46import java.util.concurrent.Executors;
47import java.util.concurrent.ScheduledExecutorService;
48import java.util.concurrent.TimeUnit;
49import java.util.stream.Collectors;
50
51import static org.onlab.util.Tools.groupedThreads;
52
53/**
54 * Component for the management of the VPLS configuration.
55 */
56@Component(immediate = true)
57public class VplsConfigManager {
58 private static final Class<VplsAppConfig> CONFIG_CLASS = VplsAppConfig.class;
59 private static final String NET_CONF_EVENT = "Received NetworkConfigEvent {}";
60 private static final String CONFIG_NULL = "VPLS configuration not defined";
61 private static final int INITIAL_RELOAD_CONFIG_DELAY = 0;
62 private static final int INITIAL_RELOAD_CONFIG_PERIOD = 1000;
63 private static final int NUM_THREADS = 1;
64 private static final String VPLS = "vpls";
65 private final Logger log = LoggerFactory.getLogger(getClass());
66
67 private final NetworkConfigListener configListener =
68 new InternalNetworkConfigListener();
69
Ray Milkeyd84f89b2018-08-17 14:54:17 -070070 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yi Tsengf4e13e32017-03-30 15:38:39 -070071 protected NetworkConfigService configService;
72
Ray Milkeyd84f89b2018-08-17 14:54:17 -070073 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yi Tsengf4e13e32017-03-30 15:38:39 -070074 protected CoreService coreService;
75
Ray Milkeyd84f89b2018-08-17 14:54:17 -070076 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yi Tsengf4e13e32017-03-30 15:38:39 -070077 protected InterfaceService interfaceService;
78
Ray Milkeyd84f89b2018-08-17 14:54:17 -070079 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yi Tsengf4e13e32017-03-30 15:38:39 -070080 protected NetworkConfigRegistry registry;
81
Ray Milkeyd84f89b2018-08-17 14:54:17 -070082 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yi Tsengf4e13e32017-03-30 15:38:39 -070083 protected Vpls vpls;
84
Ray Milkeyd84f89b2018-08-17 14:54:17 -070085 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Yi Tsengf4e13e32017-03-30 15:38:39 -070086 protected LeadershipService leadershipService;
87
88 private ScheduledExecutorService reloadExecutor =
89 Executors.newScheduledThreadPool(NUM_THREADS,
90 groupedThreads("onos/apps/vpls",
91 "config-reloader-%d",
92 log)
93 );
94
95 private ConfigFactory<ApplicationId, VplsAppConfig> vplsConfigFactory =
96 new ConfigFactory<ApplicationId, VplsAppConfig>(
97 SubjectFactories.APP_SUBJECT_FACTORY, VplsAppConfig.class, VPLS) {
98 @Override
99 public VplsAppConfig createConfig() {
100 return new VplsAppConfig();
101 }
102 };
103
104 protected ApplicationId appId;
105
106 @Activate
107 void activate() {
108 appId = coreService.registerApplication(VplsManager.VPLS_APP);
109 configService.addListener(configListener);
110
111 // Load config when VPLS service started and there is a leader for VPLS;
112 // otherwise, try again after a period.
113 reloadExecutor.scheduleAtFixedRate(() -> {
114 NodeId vplsLeaderNode = leadershipService.getLeader(appId.name());
115 if (vpls == null || vplsLeaderNode == null) {
116 return;
117 }
118 reloadConfiguration();
119 reloadExecutor.shutdown();
120 }, INITIAL_RELOAD_CONFIG_DELAY, INITIAL_RELOAD_CONFIG_PERIOD, TimeUnit.MILLISECONDS);
121 registry.registerConfigFactory(vplsConfigFactory);
122 }
123
124 @Deactivate
125 void deactivate() {
126 configService.removeListener(configListener);
127 registry.unregisterConfigFactory(vplsConfigFactory);
128 }
129
130 /**
131 * Retrieves the VPLS configuration from network configuration.
132 * Checks difference between new configuration and old configuration.
133 */
134 private synchronized void reloadConfiguration() {
135 VplsAppConfig vplsAppConfig = configService.getConfig(appId, VplsAppConfig.class);
136
137 if (vplsAppConfig == null) {
138 log.warn(CONFIG_NULL);
139
140 // If the config is null, removes all VPLS.
141 vpls.removeAllVpls();
142 return;
143 }
144
145 // If there exists a update time in the configuration; it means the
146 // configuration was pushed by VPLS store; ignore this configuration.
147 long updateTime = vplsAppConfig.updateTime();
148 if (updateTime != -1L) {
149 return;
150 }
151
152 Collection<VplsData> oldVplses = vpls.getAllVpls();
153 Collection<VplsData> newVplses;
154
155 // Generates collection of new VPLSs
156 newVplses = vplsAppConfig.vplss().stream()
157 .map(vplsConfig -> {
158 Set<Interface> interfaces = vplsConfig.ifaces().stream()
159 .map(this::getInterfaceByName)
160 .filter(Objects::nonNull)
161 .collect(Collectors.toSet());
162 VplsData vplsData = VplsData.of(vplsConfig.name(), vplsConfig.encap());
163 vplsData.addInterfaces(interfaces);
164 return vplsData;
165 }).collect(Collectors.toSet());
166
167 if (newVplses.containsAll(oldVplses) && oldVplses.containsAll(newVplses)) {
168 // no update, ignore
169 return;
170 }
171
172 // To add or update
173 newVplses.forEach(newVplsData -> {
174 boolean vplsExists = false;
175 for (VplsData oldVplsData : oldVplses) {
176 if (oldVplsData.name().equals(newVplsData.name())) {
177 vplsExists = true;
178
179 // VPLS exists; but need to be updated.
180 if (!oldVplsData.equals(newVplsData)) {
181 // Update VPLS
182 Set<Interface> newInterfaces = newVplsData.interfaces();
183 Set<Interface> oldInterfaces = oldVplsData.interfaces();
184
185 Set<Interface> ifaceToAdd = newInterfaces.stream()
186 .filter(iface -> !oldInterfaces.contains(iface))
187 .collect(Collectors.toSet());
188
189 Set<Interface> ifaceToRem = oldInterfaces.stream()
190 .filter(iface -> !newInterfaces.contains(iface))
191 .collect(Collectors.toSet());
192
193 vpls.addInterfaces(oldVplsData, ifaceToAdd);
194 vpls.removeInterfaces(oldVplsData, ifaceToRem);
195 vpls.setEncapsulationType(oldVplsData, newVplsData.encapsulationType());
196 }
197 }
198 }
199 // VPLS not exist; add new VPLS
200 if (!vplsExists) {
201 vpls.createVpls(newVplsData.name(), newVplsData.encapsulationType());
202 vpls.addInterfaces(newVplsData, newVplsData.interfaces());
203 }
204 });
205
206 // VPLS not exists in old VPLS configuration; remove it.
207 Set<VplsData> vplsToRemove = Sets.newHashSet();
208 oldVplses.forEach(oldVpls -> {
209 Set<String> newVplsNames = newVplses.stream()
210 .map(VplsData::name)
211 .collect(Collectors.toSet());
212
213 if (!newVplsNames.contains(oldVpls.name())) {
214 // To avoid ConcurrentModificationException; do remove after this
215 // iteration.
216 vplsToRemove.add(oldVpls);
217 }
218 });
219 vplsToRemove.forEach(vpls::removeVpls);
220 }
221
222 /**
223 * Gets network interface by a given name.
224 *
225 * @param interfaceName the interface name
226 * @return the network interface if there exist with the given name; null
227 * otherwise
228 */
229 private Interface getInterfaceByName(String interfaceName) {
230 return interfaceService.getInterfaces().stream()
231 .filter(iface -> iface.name().equals(interfaceName))
232 .findFirst()
233 .orElse(null);
234 }
235
236 /**
237 * Listener for VPLS configuration events.
238 * Reloads VPLS configuration when configuration added or updated.
239 * Removes all VPLS when configuration removed or unregistered.
240 */
241 private class InternalNetworkConfigListener implements NetworkConfigListener {
242 @Override
243 public void event(NetworkConfigEvent event) {
244 if (event.configClass() == CONFIG_CLASS) {
245 log.debug(NET_CONF_EVENT, event.configClass());
246 switch (event.type()) {
247 case CONFIG_ADDED:
248 case CONFIG_UPDATED:
249 reloadConfiguration();
250 break;
251 case CONFIG_REMOVED:
252 case CONFIG_UNREGISTERED:
253 vpls.removeAllVpls();
Ray Milkeyd6a67c32018-02-02 10:30:35 -0800254 break;
Yi Tsengf4e13e32017-03-30 15:38:39 -0700255 default:
256 break;
257 }
258 }
259 }
260 }
261}