blob: c9654761fba5a16f406ac16ed1a9873418b5e23b [file] [log] [blame]
alshabib3b1eadc2016-02-01 17:57:00 -08001/*
Brian O'Connoraf764bf2016-04-09 01:19:45 -07002 * Copyright 2016-present Open Networking Laboratory
alshabib3b1eadc2016-02-01 17:57:00 -08003 *
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 */
alshabib772e1582016-06-01 17:50:05 -070016package org.opencord.cordmcast;
alshabib3b1eadc2016-02-01 17:57:00 -080017
Jonathan Hartc3f84eb2016-02-19 12:44:36 -080018import com.fasterxml.jackson.databind.ObjectMapper;
19import com.fasterxml.jackson.databind.node.ArrayNode;
Jonathan Hart28271642016-02-10 16:13:54 -080020import com.fasterxml.jackson.databind.node.ObjectNode;
Jonathan Hartc3f84eb2016-02-19 12:44:36 -080021import com.google.common.collect.Lists;
alshabib3b1eadc2016-02-01 17:57:00 -080022import com.google.common.collect.Maps;
alshabib09069c92016-02-21 14:49:51 -080023import org.apache.commons.lang3.tuple.ImmutablePair;
alshabib3b1eadc2016-02-01 17:57:00 -080024import org.apache.felix.scr.annotations.Activate;
25import org.apache.felix.scr.annotations.Component;
26import org.apache.felix.scr.annotations.Deactivate;
Jonathan Hart28271642016-02-10 16:13:54 -080027import org.apache.felix.scr.annotations.Modified;
28import org.apache.felix.scr.annotations.Property;
alshabib3b1eadc2016-02-01 17:57:00 -080029import org.apache.felix.scr.annotations.Reference;
30import org.apache.felix.scr.annotations.ReferenceCardinality;
Jian Li46472d72016-03-09 10:52:49 -080031import org.glassfish.jersey.client.ClientConfig;
32import org.glassfish.jersey.client.ClientProperties;
33import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
alshabib3b1eadc2016-02-01 17:57:00 -080034import org.onlab.packet.Ethernet;
alshabib3b1eadc2016-02-01 17:57:00 -080035import org.onlab.packet.IpAddress;
36import org.onlab.packet.VlanId;
Jonathan Hart28271642016-02-10 16:13:54 -080037import org.onosproject.cfg.ComponentConfigService;
38import org.onosproject.codec.CodecService;
Jonathan Hartc3f84eb2016-02-19 12:44:36 -080039import org.onosproject.codec.JsonCodec;
alshabib772e1582016-06-01 17:50:05 -070040
alshabib3b1eadc2016-02-01 17:57:00 -080041import org.onosproject.core.ApplicationId;
42import org.onosproject.core.CoreService;
43import org.onosproject.net.ConnectPoint;
ke hanf1709e82016-08-12 10:48:17 +080044import org.onosproject.net.config.ConfigFactory;
45import org.onosproject.net.config.NetworkConfigEvent;
46import org.onosproject.net.config.NetworkConfigListener;
47import org.onosproject.net.config.NetworkConfigRegistry;
48import org.onosproject.net.config.basics.SubjectFactories;
alshabib3b1eadc2016-02-01 17:57:00 -080049import org.onosproject.net.flow.DefaultTrafficSelector;
50import org.onosproject.net.flow.DefaultTrafficTreatment;
51import org.onosproject.net.flow.TrafficSelector;
52import org.onosproject.net.flowobjective.DefaultForwardingObjective;
53import org.onosproject.net.flowobjective.DefaultNextObjective;
54import org.onosproject.net.flowobjective.FlowObjectiveService;
55import org.onosproject.net.flowobjective.ForwardingObjective;
56import org.onosproject.net.flowobjective.NextObjective;
57import org.onosproject.net.flowobjective.Objective;
58import org.onosproject.net.flowobjective.ObjectiveContext;
59import org.onosproject.net.flowobjective.ObjectiveError;
alshabib3b1eadc2016-02-01 17:57:00 -080060import org.onosproject.net.mcast.McastEvent;
61import org.onosproject.net.mcast.McastListener;
Jonathan Hart28271642016-02-10 16:13:54 -080062import org.onosproject.net.mcast.McastRoute;
alshabib3b1eadc2016-02-01 17:57:00 -080063import org.onosproject.net.mcast.McastRouteInfo;
64import org.onosproject.net.mcast.MulticastRouteService;
Jonathan Hart28271642016-02-10 16:13:54 -080065import org.onosproject.rest.AbstractWebResource;
alshabib772e1582016-06-01 17:50:05 -070066import org.opencord.cordconfig.access.AccessAgentData;
67import org.opencord.cordconfig.access.AccessDeviceData;
Charles Chane8ed8ee2016-06-13 16:37:01 -070068import org.opencord.cordconfig.CordConfigService;
Jonathan Hart28271642016-02-10 16:13:54 -080069import org.osgi.service.component.ComponentContext;
alshabib3b1eadc2016-02-01 17:57:00 -080070import org.slf4j.Logger;
ke hanf1709e82016-08-12 10:48:17 +080071import org.onosproject.incubator.net.config.basics.McastConfig;
alshabib3b1eadc2016-02-01 17:57:00 -080072
Jian Li46472d72016-03-09 10:52:49 -080073import javax.ws.rs.ProcessingException;
74import javax.ws.rs.client.Client;
75import javax.ws.rs.client.ClientBuilder;
76import javax.ws.rs.client.Entity;
77import javax.ws.rs.client.Invocation;
78import javax.ws.rs.client.WebTarget;
Jonathan Hartc3f84eb2016-02-19 12:44:36 -080079import javax.ws.rs.core.MediaType;
80import java.io.IOException;
Jonathan Hart28271642016-02-10 16:13:54 -080081import java.util.Dictionary;
Jonathan Hartc3f84eb2016-02-19 12:44:36 -080082import java.util.List;
alshabib3b1eadc2016-02-01 17:57:00 -080083import java.util.Map;
Jonathan Hart0c194962016-05-23 17:08:15 -070084import java.util.Optional;
alshabibfc1cb032016-02-17 15:37:56 -080085import java.util.Properties;
Jonathan Hart28271642016-02-10 16:13:54 -080086import java.util.concurrent.atomic.AtomicBoolean;
alshabib3b1eadc2016-02-01 17:57:00 -080087
Jonathan Hartc3f84eb2016-02-19 12:44:36 -080088import static com.google.common.base.Preconditions.checkNotNull;
alshabibfc1cb032016-02-17 15:37:56 -080089import static com.google.common.base.Strings.isNullOrEmpty;
Jonathan Hart28271642016-02-10 16:13:54 -080090import static com.google.common.net.MediaType.JSON_UTF_8;
alshabibfc1cb032016-02-17 15:37:56 -080091import static org.onlab.util.Tools.get;
alshabib3b1eadc2016-02-01 17:57:00 -080092import static org.slf4j.LoggerFactory.getLogger;
93
94/**
Jonathan Hartc3f84eb2016-02-19 12:44:36 -080095 * CORD multicast provisioning application. Operates by listening to
Jonathan Hart28271642016-02-10 16:13:54 -080096 * events on the multicast rib and provisioning groups to program multicast
alshabib3b1eadc2016-02-01 17:57:00 -080097 * flows on the dataplane.
98 */
99@Component(immediate = true)
100public class CordMcast {
Charles Chanf867c4b2017-01-20 11:22:25 -0800101 private static final String APP_NAME = "org.opencord.cordmcast";
alshabib3b1eadc2016-02-01 17:57:00 -0800102
Jonathan Hart0c194962016-05-23 17:08:15 -0700103 private final Logger log = getLogger(getClass());
alshabib09069c92016-02-21 14:49:51 -0800104
Jonathan Hart0c194962016-05-23 17:08:15 -0700105 private static final int DEFAULT_REST_TIMEOUT_MS = 1000;
alshabib09069c92016-02-21 14:49:51 -0800106 private static final int DEFAULT_PRIORITY = 500;
alshabib3b1eadc2016-02-01 17:57:00 -0800107 private static final short DEFAULT_MCAST_VLAN = 4000;
alshabibfc1cb032016-02-17 15:37:56 -0800108 private static final String DEFAULT_SYNC_HOST = "10.90.0.8:8181";
109 private static final String DEFAULT_USER = "karaf";
110 private static final String DEFAULT_PASSWORD = "karaf";
alshabib09069c92016-02-21 14:49:51 -0800111 private static final boolean DEFAULT_VLAN_ENABLED = true;
alshabibfc1cb032016-02-17 15:37:56 -0800112
alshabib3b1eadc2016-02-01 17:57:00 -0800113 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
114 protected MulticastRouteService mcastService;
115
116 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabib3b1eadc2016-02-01 17:57:00 -0800117 protected FlowObjectiveService flowObjectiveService;
118
119 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
120 protected CoreService coreService;
121
Jonathan Hart28271642016-02-10 16:13:54 -0800122 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
123 protected CodecService codecService;
124
125 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
126 protected ComponentConfigService componentConfigService;
127
alshabib09069c92016-02-21 14:49:51 -0800128 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jonathan Hart0c194962016-05-23 17:08:15 -0700129 protected CordConfigService cordConfigService;
alshabib09069c92016-02-21 14:49:51 -0800130
ke hanf1709e82016-08-12 10:48:17 +0800131 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
132 protected NetworkConfigRegistry networkConfig;
133
alshabib3b1eadc2016-02-01 17:57:00 -0800134 protected McastListener listener = new InternalMulticastListener();
ke hanf1709e82016-08-12 10:48:17 +0800135 private InternalNetworkConfigListener configListener =
136 new InternalNetworkConfigListener();
alshabib3b1eadc2016-02-01 17:57:00 -0800137
alshabib3b1eadc2016-02-01 17:57:00 -0800138 //TODO: move this to a ec map
139 private Map<IpAddress, Integer> groups = Maps.newConcurrentMap();
140
alshabib3b1eadc2016-02-01 17:57:00 -0800141 private ApplicationId appId;
ke hanf1709e82016-08-12 10:48:17 +0800142 private ApplicationId coreAppId;
alshabibfc1cb032016-02-17 15:37:56 -0800143 private int mcastVlan = DEFAULT_MCAST_VLAN;
alshabib3b1eadc2016-02-01 17:57:00 -0800144
alshabib09069c92016-02-21 14:49:51 -0800145 @Property(name = "vlanEnabled", boolValue = DEFAULT_VLAN_ENABLED,
146 label = "Use vlan for multicast traffic?")
147 private boolean vlanEnabled = DEFAULT_VLAN_ENABLED;
alshabibfc1cb032016-02-17 15:37:56 -0800148
149 @Property(name = "priority", intValue = DEFAULT_PRIORITY,
150 label = "Priority for multicast rules")
alshabib3b1eadc2016-02-01 17:57:00 -0800151 private int priority = DEFAULT_PRIORITY;
152
alshabibfc1cb032016-02-17 15:37:56 -0800153 @Property(name = "syncHost", value = DEFAULT_SYNC_HOST,
Jonathan Hart28271642016-02-10 16:13:54 -0800154 label = "host:port to synchronize routes to")
ke hand851dce2017-04-28 13:58:02 +0800155 private String syncHost = null;
Jonathan Hart28271642016-02-10 16:13:54 -0800156
157 @Property(name = "username", value = DEFAULT_USER,
158 label = "Username for REST password authentication")
159 private String user = DEFAULT_USER;
160
161 @Property(name = "password", value = DEFAULT_PASSWORD,
162 label = "Password for REST authentication")
163 private String password = DEFAULT_PASSWORD;
164
165 private String fabricOnosUrl;
ke hanf1709e82016-08-12 10:48:17 +0800166 private static final Class<McastConfig> CORD_MCAST_CONFIG_CLASS =
167 McastConfig.class;
168
169 private ConfigFactory<ApplicationId, McastConfig> cordMcastConfigFactory =
170 new ConfigFactory<ApplicationId, McastConfig>(
171 SubjectFactories.APP_SUBJECT_FACTORY, CORD_MCAST_CONFIG_CLASS, "multicast") {
172 @Override
173 public McastConfig createConfig() {
174 return new McastConfig();
175 }
176 };
Jonathan Hart28271642016-02-10 16:13:54 -0800177
alshabib3b1eadc2016-02-01 17:57:00 -0800178 @Activate
Jonathan Hart435ffc42016-02-19 10:32:05 -0800179 public void activate(ComponentContext context) {
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800180 componentConfigService.registerProperties(getClass());
Jonathan Hart435ffc42016-02-19 10:32:05 -0800181 modified(context);
182
Charles Chanf867c4b2017-01-20 11:22:25 -0800183 appId = coreService.registerApplication(APP_NAME);
ke hanf1709e82016-08-12 10:48:17 +0800184 coreAppId = coreService.registerApplication(CoreService.CORE_APP_NAME);
Jonathan Hart28271642016-02-10 16:13:54 -0800185
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800186 clearRemoteRoutes();
ke hanf1709e82016-08-12 10:48:17 +0800187 networkConfig.registerConfigFactory(cordMcastConfigFactory);
188 networkConfig.addListener(configListener);
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800189 mcastService.addListener(listener);
190
alshabib09069c92016-02-21 14:49:51 -0800191 mcastService.getRoutes().stream()
192 .map(r -> new ImmutablePair<>(r, mcastService.fetchSinks(r)))
193 .filter(pair -> pair.getRight() != null && !pair.getRight().isEmpty())
194 .forEach(pair -> pair.getRight().forEach(sink -> provisionGroup(pair.getLeft(),
Jian Li46472d72016-03-09 10:52:49 -0800195 sink)));
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800196
ke hanf1709e82016-08-12 10:48:17 +0800197 McastConfig config = networkConfig.getConfig(coreAppId, CORD_MCAST_CONFIG_CLASS);
198 if (config != null) {
199 mcastVlan = config.egressVlan().toShort();
200 }
201
alshabib3b1eadc2016-02-01 17:57:00 -0800202 log.info("Started");
203 }
204
205 @Deactivate
206 public void deactivate() {
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800207 componentConfigService.unregisterProperties(getClass(), false);
alshabib3b1eadc2016-02-01 17:57:00 -0800208 mcastService.removeListener(listener);
ke hanf1709e82016-08-12 10:48:17 +0800209 networkConfig.removeListener(configListener);
alshabib3b1eadc2016-02-01 17:57:00 -0800210 log.info("Stopped");
211 }
212
Jonathan Hart28271642016-02-10 16:13:54 -0800213 @Modified
214 public void modified(ComponentContext context) {
alshabibfc1cb032016-02-17 15:37:56 -0800215 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
216
alshabibfc1cb032016-02-17 15:37:56 -0800217 try {
218 String s = get(properties, "username");
219 user = isNullOrEmpty(s) ? DEFAULT_USER : s.trim();
220
221 s = get(properties, "password");
222 password = isNullOrEmpty(s) ? DEFAULT_PASSWORD : s.trim();
223
alshabibfc1cb032016-02-17 15:37:56 -0800224 s = get(properties, "vlanEnabled");
alshabib09069c92016-02-21 14:49:51 -0800225 vlanEnabled = isNullOrEmpty(s) ? DEFAULT_VLAN_ENABLED : Boolean.parseBoolean(s.trim());
alshabibfc1cb032016-02-17 15:37:56 -0800226
227 s = get(properties, "priority");
228 priority = isNullOrEmpty(s) ? DEFAULT_PRIORITY : Integer.parseInt(s.trim());
229
Jonathan Hart0212f642016-02-20 11:32:43 -0800230 s = get(properties, "syncHost");
ke hand851dce2017-04-28 13:58:02 +0800231 syncHost = isNullOrEmpty(s) ? null : s.trim();
alshabibfc1cb032016-02-17 15:37:56 -0800232 } catch (Exception e) {
233 user = DEFAULT_USER;
234 password = DEFAULT_PASSWORD;
ke hand851dce2017-04-28 13:58:02 +0800235 syncHost = null;
alshabibfc1cb032016-02-17 15:37:56 -0800236 mcastVlan = DEFAULT_MCAST_VLAN;
237 vlanEnabled = false;
238 priority = DEFAULT_PRIORITY;
239 }
Jonathan Hart0212f642016-02-20 11:32:43 -0800240 fabricOnosUrl = createRemoteUrl(syncHost);
241 }
242
243 private static String createRemoteUrl(String remoteHost) {
244 return "http://" + remoteHost + "/onos/v1/mcast";
Jonathan Hart28271642016-02-10 16:13:54 -0800245 }
246
alshabib3b1eadc2016-02-01 17:57:00 -0800247 private class InternalMulticastListener implements McastListener {
248 @Override
249 public void event(McastEvent event) {
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800250 McastRouteInfo info = event.subject();
alshabib3b1eadc2016-02-01 17:57:00 -0800251 switch (event.type()) {
252 case ROUTE_ADDED:
253 break;
254 case ROUTE_REMOVED:
255 break;
256 case SOURCE_ADDED:
257 break;
258 case SINK_ADDED:
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800259 if (!info.sink().isPresent()) {
260 log.warn("No sink given after sink added event: {}", info);
261 return;
262 }
263 provisionGroup(info.route(), info.sink().get());
alshabib3b1eadc2016-02-01 17:57:00 -0800264 break;
265 case SINK_REMOVED:
alshabibfc1cb032016-02-17 15:37:56 -0800266 unprovisionGroup(event.subject());
alshabib3b1eadc2016-02-01 17:57:00 -0800267 break;
268 default:
269 log.warn("Unknown mcast event {}", event.type());
270 }
271 }
272 }
273
alshabibfc1cb032016-02-17 15:37:56 -0800274 private void unprovisionGroup(McastRouteInfo info) {
Jonathan Hart718c0452016-02-18 15:56:22 -0800275 if (info.sinks().isEmpty()) {
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800276 removeRemoteRoute(info.route());
Jonathan Hart718c0452016-02-18 15:56:22 -0800277 }
278
alshabibfc1cb032016-02-17 15:37:56 -0800279 if (!info.sink().isPresent()) {
280 log.warn("No sink given after sink removed event: {}", info);
281 return;
282 }
283 ConnectPoint loc = info.sink().get();
284
285 NextObjective next = DefaultNextObjective.builder()
286 .fromApp(appId)
287 .addTreatment(DefaultTrafficTreatment.builder().setOutput(loc.port()).build())
288 .withType(NextObjective.Type.BROADCAST)
289 .withId(groups.get(info.route().group()))
290 .removeFromExisting(new ObjectiveContext() {
291 @Override
292 public void onSuccess(Objective objective) {
293 //TODO: change to debug
294 log.info("Next Objective {} installed", objective.id());
295 }
296
297 @Override
298 public void onError(Objective objective, ObjectiveError error) {
299 //TODO: change to debug
300 log.info("Next Objective {} failed, because {}",
Jian Li46472d72016-03-09 10:52:49 -0800301 objective.id(),
302 error);
alshabibfc1cb032016-02-17 15:37:56 -0800303 }
304 });
305
306 flowObjectiveService.next(loc.deviceId(), next);
alshabibfc1cb032016-02-17 15:37:56 -0800307 }
308
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800309 private void provisionGroup(McastRoute route, ConnectPoint sink) {
310 checkNotNull(route, "Route cannot be null");
311 checkNotNull(sink, "Sink cannot be null");
alshabib3b1eadc2016-02-01 17:57:00 -0800312
Jonathan Hart0c194962016-05-23 17:08:15 -0700313 Optional<AccessDeviceData> oltInfo = cordConfigService.getAccessDevice(sink.deviceId());
alshabib09069c92016-02-21 14:49:51 -0800314
Jonathan Hart0c194962016-05-23 17:08:15 -0700315 if (!oltInfo.isPresent()) {
alshabib09069c92016-02-21 14:49:51 -0800316 log.warn("Unknown OLT device : {}", sink.deviceId());
317 return;
318 }
319
Jonathan Hart28271642016-02-10 16:13:54 -0800320 final AtomicBoolean sync = new AtomicBoolean(false);
alshabib3b1eadc2016-02-01 17:57:00 -0800321
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800322 Integer nextId = groups.computeIfAbsent(route.group(), (g) -> {
Zsolt Harasztiab436262016-02-25 09:39:10 -0800323 Integer id = flowObjectiveService.allocateNextId();
alshabib3b1eadc2016-02-01 17:57:00 -0800324
alshabibfc1cb032016-02-17 15:37:56 -0800325 NextObjective next = DefaultNextObjective.builder()
326 .fromApp(appId)
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800327 .addTreatment(DefaultTrafficTreatment.builder().setOutput(sink.port()).build())
alshabibfc1cb032016-02-17 15:37:56 -0800328 .withType(NextObjective.Type.BROADCAST)
329 .withId(id)
330 .add(new ObjectiveContext() {
331 @Override
332 public void onSuccess(Objective objective) {
333 //TODO: change to debug
334 log.info("Next Objective {} installed", objective.id());
335 }
336
337 @Override
338 public void onError(Objective objective, ObjectiveError error) {
339 //TODO: change to debug
340 log.info("Next Objective {} failed, because {}",
Jian Li46472d72016-03-09 10:52:49 -0800341 objective.id(),
342 error);
alshabibfc1cb032016-02-17 15:37:56 -0800343 }
344 });
345
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800346 flowObjectiveService.next(sink.deviceId(), next);
alshabibfc1cb032016-02-17 15:37:56 -0800347
348 TrafficSelector.Builder mcast = DefaultTrafficSelector.builder()
Jonathan Hart0c194962016-05-23 17:08:15 -0700349 .matchInPort(oltInfo.get().uplink())
alshabib3b1eadc2016-02-01 17:57:00 -0800350 .matchEthType(Ethernet.TYPE_IPV4)
alshabibfc1cb032016-02-17 15:37:56 -0800351 .matchIPDst(g.toIpPrefix());
352
alshabibfc1cb032016-02-17 15:37:56 -0800353 if (vlanEnabled) {
354 mcast.matchVlanId(VlanId.vlanId((short) mcastVlan));
355 }
alshabib3b1eadc2016-02-01 17:57:00 -0800356
alshabib3b1eadc2016-02-01 17:57:00 -0800357 ForwardingObjective fwd = DefaultForwardingObjective.builder()
358 .fromApp(appId)
359 .nextStep(id)
360 .makePermanent()
361 .withFlag(ForwardingObjective.Flag.VERSATILE)
362 .withPriority(priority)
alshabibfc1cb032016-02-17 15:37:56 -0800363 .withSelector(mcast.build())
alshabib3b1eadc2016-02-01 17:57:00 -0800364 .add(new ObjectiveContext() {
365 @Override
366 public void onSuccess(Objective objective) {
367 //TODO: change to debug
368 log.info("Forwarding objective installed {}", objective);
369 }
370
371 @Override
372 public void onError(Objective objective, ObjectiveError error) {
373 //TODO: change to debug
374 log.info("Forwarding objective failed {}", objective);
375 }
376 });
377
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800378 flowObjectiveService.forward(sink.deviceId(), fwd);
alshabib3b1eadc2016-02-01 17:57:00 -0800379
Jonathan Hart28271642016-02-10 16:13:54 -0800380 sync.set(true);
381
alshabib09069c92016-02-21 14:49:51 -0800382 return id;
alshabib3b1eadc2016-02-01 17:57:00 -0800383 });
384
alshabibfc1cb032016-02-17 15:37:56 -0800385 if (!sync.get()) {
386 NextObjective next = DefaultNextObjective.builder()
387 .fromApp(appId)
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800388 .addTreatment(DefaultTrafficTreatment.builder().setOutput(sink.port()).build())
alshabibfc1cb032016-02-17 15:37:56 -0800389 .withType(NextObjective.Type.BROADCAST)
390 .withId(nextId)
391 .addToExisting(new ObjectiveContext() {
392 @Override
393 public void onSuccess(Objective objective) {
394 //TODO: change to debug
395 log.info("Next Objective {} installed", objective.id());
396 }
alshabib3b1eadc2016-02-01 17:57:00 -0800397
alshabibfc1cb032016-02-17 15:37:56 -0800398 @Override
399 public void onError(Objective objective, ObjectiveError error) {
400 //TODO: change to debug
401 log.info("Next Objective {} failed, because {}",
Jian Li46472d72016-03-09 10:52:49 -0800402 objective.id(),
403 error);
alshabibfc1cb032016-02-17 15:37:56 -0800404 }
405 });
alshabib3b1eadc2016-02-01 17:57:00 -0800406
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800407 flowObjectiveService.next(sink.deviceId(), next);
alshabibfc1cb032016-02-17 15:37:56 -0800408 }
Jonathan Hart28271642016-02-10 16:13:54 -0800409
Jonathan Hart0c194962016-05-23 17:08:15 -0700410 addRemoteRoute(route, sink);
alshabib3b1eadc2016-02-01 17:57:00 -0800411 }
412
Jonathan Hart0c194962016-05-23 17:08:15 -0700413 private void addRemoteRoute(McastRoute route, ConnectPoint inPort) {
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800414 checkNotNull(route);
Jonathan Hart28271642016-02-10 16:13:54 -0800415 if (syncHost == null) {
416 log.warn("No host configured for synchronization; route will be dropped");
417 return;
418 }
419
Jonathan Hart0c194962016-05-23 17:08:15 -0700420 Optional<AccessAgentData> accessAgent = cordConfigService.getAccessAgent(inPort.deviceId());
421 if (!accessAgent.isPresent()) {
422 log.warn("No accessAgent config found for in port {}", inPort);
423 return;
424 }
425
426 if (!accessAgent.get().getOltConnectPoint(inPort).isPresent()) {
427 log.warn("No OLT configured for in port {}", inPort);
428 return;
429 }
430
431 ConnectPoint oltConnectPoint = accessAgent.get().getOltConnectPoint(inPort).get();
432
Jonathan Hart0212f642016-02-20 11:32:43 -0800433 log.debug("Sending route {} to other ONOS {}", route, fabricOnosUrl);
Jonathan Hart28271642016-02-10 16:13:54 -0800434
Jian Li46472d72016-03-09 10:52:49 -0800435 Invocation.Builder builder = getClientBuilder(fabricOnosUrl);
Jonathan Hart28271642016-02-10 16:13:54 -0800436
437 ObjectNode json = codecService.getCodec(McastRoute.class)
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800438 .encode(route, new AbstractWebResource());
Jonathan Hart0212f642016-02-20 11:32:43 -0800439
440 try {
Jian Li46472d72016-03-09 10:52:49 -0800441 builder.post(Entity.json(json.toString()));
Jonathan Hart0c194962016-05-23 17:08:15 -0700442
443 builder = getClientBuilder(fabricOnosUrl + "/sinks/" + route.group() + "/" + route.source());
444 ObjectMapper mapper = new ObjectMapper();
445 ObjectNode obj = mapper.createObjectNode();
446 obj.putArray("sinks").add(oltConnectPoint.deviceId() + "/" + oltConnectPoint.port());
447
448 builder.post(Entity.json(obj.toString()));
Jian Li46472d72016-03-09 10:52:49 -0800449 } catch (ProcessingException e) {
Jonathan Hart0212f642016-02-20 11:32:43 -0800450 log.warn("Unable to send route to remote controller: {}", e.getMessage());
451 }
alshabib3b1eadc2016-02-01 17:57:00 -0800452 }
Jonathan Hart28271642016-02-10 16:13:54 -0800453
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800454 private void removeRemoteRoute(McastRoute route) {
Jonathan Hart718c0452016-02-18 15:56:22 -0800455 if (syncHost == null) {
456 log.warn("No host configured for synchronization; route will be dropped");
457 return;
458 }
459
Jonathan Hart0212f642016-02-20 11:32:43 -0800460 log.debug("Removing route {} from other ONOS {}", route, fabricOnosUrl);
Jonathan Hart718c0452016-02-18 15:56:22 -0800461
Jian Li46472d72016-03-09 10:52:49 -0800462 Invocation.Builder builder = getClientBuilder(fabricOnosUrl)
463 .property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true);
Jonathan Hart718c0452016-02-18 15:56:22 -0800464
465 ObjectNode json = codecService.getCodec(McastRoute.class)
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800466 .encode(route, new AbstractWebResource());
Jian Li46472d72016-03-09 10:52:49 -0800467
468 builder.method("DELETE", Entity.entity(json.asText(),
469 MediaType.APPLICATION_OCTET_STREAM));
Jonathan Hart718c0452016-02-18 15:56:22 -0800470 }
471
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800472 private void clearRemoteRoutes() {
473 if (syncHost == null) {
474 log.warn("No host configured for synchronization");
475 return;
476 }
477
Jonathan Hart0212f642016-02-20 11:32:43 -0800478 log.debug("Clearing remote multicast routes from {}", fabricOnosUrl);
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800479
Jian Li46472d72016-03-09 10:52:49 -0800480 Invocation.Builder builder = getClientBuilder(fabricOnosUrl);
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800481 List<McastRoute> mcastRoutes = Lists.newArrayList();
Jonathan Hart0212f642016-02-20 11:32:43 -0800482
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800483 try {
Jonathan Hart0212f642016-02-20 11:32:43 -0800484 String response = builder
485 .accept(MediaType.APPLICATION_JSON_TYPE)
486 .get(String.class);
487
488 JsonCodec<McastRoute> routeCodec = codecService.getCodec(McastRoute.class);
489 ObjectMapper mapper = new ObjectMapper();
490
491
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800492 ObjectNode node = (ObjectNode) mapper.readTree(response);
493 ArrayNode list = (ArrayNode) node.path("routes");
494
495 list.forEach(n -> mcastRoutes.add(
496 routeCodec.decode((ObjectNode) n, new AbstractWebResource())));
Jonathan Hart0212f642016-02-20 11:32:43 -0800497
Jonathan Hart0c194962016-05-23 17:08:15 -0700498 } catch (IOException | ProcessingException e) {
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800499 log.warn("Error clearing remote routes", e);
500 }
501
502 mcastRoutes.forEach(this::removeRemoteRoute);
503 }
504
Jian Li46472d72016-03-09 10:52:49 -0800505 private Invocation.Builder getClientBuilder(String uri) {
506 ClientConfig config = new ClientConfig();
507 Client client = ClientBuilder.newClient(config);
508
509 client.property(ClientProperties.CONNECT_TIMEOUT, DEFAULT_REST_TIMEOUT_MS);
510 client.property(ClientProperties.READ_TIMEOUT, DEFAULT_REST_TIMEOUT_MS);
511 client.register(HttpAuthenticationFeature.basic(user, password));
512
513 WebTarget wt = client.target(uri);
514 return wt.request(JSON_UTF_8.toString());
Jonathan Hart28271642016-02-10 16:13:54 -0800515 }
516
ke hanf1709e82016-08-12 10:48:17 +0800517 private class InternalNetworkConfigListener implements NetworkConfigListener {
518 @Override
519 public void event(NetworkConfigEvent event) {
520 switch (event.type()) {
521
522 case CONFIG_ADDED:
523 case CONFIG_UPDATED:
524 if (event.configClass().equals(CORD_MCAST_CONFIG_CLASS)) {
525 McastConfig config = networkConfig.getConfig(coreAppId, CORD_MCAST_CONFIG_CLASS);
526 if (config != null) {
527 mcastVlan = config.egressVlan().toShort();
528 }
529 }
530 break;
531 case CONFIG_REGISTERED:
532 case CONFIG_UNREGISTERED:
533 case CONFIG_REMOVED:
534 break;
535 default:
536 break;
537 }
538 }
539 }
alshabib3b1eadc2016-02-01 17:57:00 -0800540}
ke hanf1709e82016-08-12 10:48:17 +0800541