blob: 1bf6718af99bc4b7447808746b9da064e09a6202 [file] [log] [blame]
alshabib3b1eadc2016-02-01 17:57:00 -08001/*
Brian O'Connorcf85aa82017-08-03 22:46:01 -07002 * Copyright 2016-present Open Networking Foundation
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 han9590c812017-02-28 15:02:26 +080044import org.onosproject.net.DeviceId;
45import org.onosproject.net.PortNumber;
ke hanf1709e82016-08-12 10:48:17 +080046import org.onosproject.net.config.ConfigFactory;
47import org.onosproject.net.config.NetworkConfigEvent;
48import org.onosproject.net.config.NetworkConfigListener;
49import org.onosproject.net.config.NetworkConfigRegistry;
50import org.onosproject.net.config.basics.SubjectFactories;
alshabib3b1eadc2016-02-01 17:57:00 -080051import org.onosproject.net.flow.DefaultTrafficSelector;
52import org.onosproject.net.flow.DefaultTrafficTreatment;
ke han9590c812017-02-28 15:02:26 +080053import org.onosproject.net.flow.FlowRuleService;
alshabib3b1eadc2016-02-01 17:57:00 -080054import org.onosproject.net.flow.TrafficSelector;
55import org.onosproject.net.flowobjective.DefaultForwardingObjective;
56import org.onosproject.net.flowobjective.DefaultNextObjective;
57import org.onosproject.net.flowobjective.FlowObjectiveService;
58import org.onosproject.net.flowobjective.ForwardingObjective;
59import org.onosproject.net.flowobjective.NextObjective;
60import org.onosproject.net.flowobjective.Objective;
61import org.onosproject.net.flowobjective.ObjectiveContext;
62import org.onosproject.net.flowobjective.ObjectiveError;
alshabib3b1eadc2016-02-01 17:57:00 -080063import org.onosproject.net.mcast.McastEvent;
64import org.onosproject.net.mcast.McastListener;
Jonathan Hart28271642016-02-10 16:13:54 -080065import org.onosproject.net.mcast.McastRoute;
alshabib3b1eadc2016-02-01 17:57:00 -080066import org.onosproject.net.mcast.McastRouteInfo;
67import org.onosproject.net.mcast.MulticastRouteService;
Jonathan Hart28271642016-02-10 16:13:54 -080068import org.onosproject.rest.AbstractWebResource;
alshabib772e1582016-06-01 17:50:05 -070069import org.opencord.cordconfig.access.AccessAgentData;
70import org.opencord.cordconfig.access.AccessDeviceData;
Charles Chane8ed8ee2016-06-13 16:37:01 -070071import org.opencord.cordconfig.CordConfigService;
Jonathan Hart28271642016-02-10 16:13:54 -080072import org.osgi.service.component.ComponentContext;
alshabib3b1eadc2016-02-01 17:57:00 -080073import org.slf4j.Logger;
ke hanf1709e82016-08-12 10:48:17 +080074import org.onosproject.incubator.net.config.basics.McastConfig;
alshabib3b1eadc2016-02-01 17:57:00 -080075
Jian Li46472d72016-03-09 10:52:49 -080076import javax.ws.rs.ProcessingException;
77import javax.ws.rs.client.Client;
78import javax.ws.rs.client.ClientBuilder;
79import javax.ws.rs.client.Entity;
80import javax.ws.rs.client.Invocation;
81import javax.ws.rs.client.WebTarget;
Jonathan Hartc3f84eb2016-02-19 12:44:36 -080082import javax.ws.rs.core.MediaType;
83import java.io.IOException;
Jonathan Hart28271642016-02-10 16:13:54 -080084import java.util.Dictionary;
Jonathan Hartc3f84eb2016-02-19 12:44:36 -080085import java.util.List;
alshabib3b1eadc2016-02-01 17:57:00 -080086import java.util.Map;
ke han9590c812017-02-28 15:02:26 +080087import java.util.Objects;
Jonathan Hart0c194962016-05-23 17:08:15 -070088import java.util.Optional;
alshabibfc1cb032016-02-17 15:37:56 -080089import java.util.Properties;
Jonathan Hart28271642016-02-10 16:13:54 -080090import java.util.concurrent.atomic.AtomicBoolean;
alshabib3b1eadc2016-02-01 17:57:00 -080091
Jonathan Hartc3f84eb2016-02-19 12:44:36 -080092import static com.google.common.base.Preconditions.checkNotNull;
alshabibfc1cb032016-02-17 15:37:56 -080093import static com.google.common.base.Strings.isNullOrEmpty;
Jonathan Hart28271642016-02-10 16:13:54 -080094import static com.google.common.net.MediaType.JSON_UTF_8;
alshabibfc1cb032016-02-17 15:37:56 -080095import static org.onlab.util.Tools.get;
alshabib3b1eadc2016-02-01 17:57:00 -080096import static org.slf4j.LoggerFactory.getLogger;
97
98/**
Jonathan Hartc3f84eb2016-02-19 12:44:36 -080099 * CORD multicast provisioning application. Operates by listening to
Jonathan Hart28271642016-02-10 16:13:54 -0800100 * events on the multicast rib and provisioning groups to program multicast
alshabib3b1eadc2016-02-01 17:57:00 -0800101 * flows on the dataplane.
102 */
103@Component(immediate = true)
104public class CordMcast {
Charles Chanf867c4b2017-01-20 11:22:25 -0800105 private static final String APP_NAME = "org.opencord.cordmcast";
alshabib3b1eadc2016-02-01 17:57:00 -0800106
Jonathan Hart0c194962016-05-23 17:08:15 -0700107 private final Logger log = getLogger(getClass());
alshabib09069c92016-02-21 14:49:51 -0800108
Jonathan Hart0c194962016-05-23 17:08:15 -0700109 private static final int DEFAULT_REST_TIMEOUT_MS = 1000;
alshabib09069c92016-02-21 14:49:51 -0800110 private static final int DEFAULT_PRIORITY = 500;
alshabib3b1eadc2016-02-01 17:57:00 -0800111 private static final short DEFAULT_MCAST_VLAN = 4000;
ke han9590c812017-02-28 15:02:26 +0800112 private static final String DEFAULT_SYNC_HOST = "";
alshabibfc1cb032016-02-17 15:37:56 -0800113 private static final String DEFAULT_USER = "karaf";
114 private static final String DEFAULT_PASSWORD = "karaf";
alshabib09069c92016-02-21 14:49:51 -0800115 private static final boolean DEFAULT_VLAN_ENABLED = true;
alshabibfc1cb032016-02-17 15:37:56 -0800116
alshabib3b1eadc2016-02-01 17:57:00 -0800117 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
118 protected MulticastRouteService mcastService;
119
120 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
alshabib3b1eadc2016-02-01 17:57:00 -0800121 protected FlowObjectiveService flowObjectiveService;
122
123 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
124 protected CoreService coreService;
125
Jonathan Hart28271642016-02-10 16:13:54 -0800126 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
127 protected CodecService codecService;
128
129 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
130 protected ComponentConfigService componentConfigService;
131
alshabib09069c92016-02-21 14:49:51 -0800132 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
Jonathan Hart0c194962016-05-23 17:08:15 -0700133 protected CordConfigService cordConfigService;
alshabib09069c92016-02-21 14:49:51 -0800134
ke hanf1709e82016-08-12 10:48:17 +0800135 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
136 protected NetworkConfigRegistry networkConfig;
137
ke han9590c812017-02-28 15:02:26 +0800138 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
139 protected FlowRuleService flowRuleService;
140
alshabib3b1eadc2016-02-01 17:57:00 -0800141 protected McastListener listener = new InternalMulticastListener();
ke hanf1709e82016-08-12 10:48:17 +0800142 private InternalNetworkConfigListener configListener =
143 new InternalNetworkConfigListener();
alshabib3b1eadc2016-02-01 17:57:00 -0800144
alshabib3b1eadc2016-02-01 17:57:00 -0800145 //TODO: move this to a ec map
ke han9590c812017-02-28 15:02:26 +0800146 private Map<NextKey, Integer> groups = Maps.newConcurrentMap();
alshabib3b1eadc2016-02-01 17:57:00 -0800147
alshabib3b1eadc2016-02-01 17:57:00 -0800148 private ApplicationId appId;
ke hanf1709e82016-08-12 10:48:17 +0800149 private ApplicationId coreAppId;
alshabibfc1cb032016-02-17 15:37:56 -0800150 private int mcastVlan = DEFAULT_MCAST_VLAN;
alshabib3b1eadc2016-02-01 17:57:00 -0800151
alshabib09069c92016-02-21 14:49:51 -0800152 @Property(name = "vlanEnabled", boolValue = DEFAULT_VLAN_ENABLED,
153 label = "Use vlan for multicast traffic?")
154 private boolean vlanEnabled = DEFAULT_VLAN_ENABLED;
alshabibfc1cb032016-02-17 15:37:56 -0800155
156 @Property(name = "priority", intValue = DEFAULT_PRIORITY,
157 label = "Priority for multicast rules")
alshabib3b1eadc2016-02-01 17:57:00 -0800158 private int priority = DEFAULT_PRIORITY;
159
alshabibfc1cb032016-02-17 15:37:56 -0800160 @Property(name = "syncHost", value = DEFAULT_SYNC_HOST,
Jonathan Hart28271642016-02-10 16:13:54 -0800161 label = "host:port to synchronize routes to")
ke hand851dce2017-04-28 13:58:02 +0800162 private String syncHost = null;
Jonathan Hart28271642016-02-10 16:13:54 -0800163
164 @Property(name = "username", value = DEFAULT_USER,
165 label = "Username for REST password authentication")
166 private String user = DEFAULT_USER;
167
168 @Property(name = "password", value = DEFAULT_PASSWORD,
169 label = "Password for REST authentication")
170 private String password = DEFAULT_PASSWORD;
171
172 private String fabricOnosUrl;
ke hanf1709e82016-08-12 10:48:17 +0800173 private static final Class<McastConfig> CORD_MCAST_CONFIG_CLASS =
174 McastConfig.class;
175
176 private ConfigFactory<ApplicationId, McastConfig> cordMcastConfigFactory =
177 new ConfigFactory<ApplicationId, McastConfig>(
178 SubjectFactories.APP_SUBJECT_FACTORY, CORD_MCAST_CONFIG_CLASS, "multicast") {
179 @Override
180 public McastConfig createConfig() {
181 return new McastConfig();
182 }
183 };
Jonathan Hart28271642016-02-10 16:13:54 -0800184
alshabib3b1eadc2016-02-01 17:57:00 -0800185 @Activate
Jonathan Hart435ffc42016-02-19 10:32:05 -0800186 public void activate(ComponentContext context) {
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800187 componentConfigService.registerProperties(getClass());
Jonathan Hart435ffc42016-02-19 10:32:05 -0800188 modified(context);
189
Charles Chanf867c4b2017-01-20 11:22:25 -0800190 appId = coreService.registerApplication(APP_NAME);
ke hanf1709e82016-08-12 10:48:17 +0800191 coreAppId = coreService.registerApplication(CoreService.CORE_APP_NAME);
Jonathan Hart28271642016-02-10 16:13:54 -0800192
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800193 clearRemoteRoutes();
ke hanf1709e82016-08-12 10:48:17 +0800194 networkConfig.registerConfigFactory(cordMcastConfigFactory);
195 networkConfig.addListener(configListener);
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800196 mcastService.addListener(listener);
197
alshabib09069c92016-02-21 14:49:51 -0800198 mcastService.getRoutes().stream()
199 .map(r -> new ImmutablePair<>(r, mcastService.fetchSinks(r)))
200 .filter(pair -> pair.getRight() != null && !pair.getRight().isEmpty())
201 .forEach(pair -> pair.getRight().forEach(sink -> provisionGroup(pair.getLeft(),
Jian Li46472d72016-03-09 10:52:49 -0800202 sink)));
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800203
ke hanf1709e82016-08-12 10:48:17 +0800204 McastConfig config = networkConfig.getConfig(coreAppId, CORD_MCAST_CONFIG_CLASS);
205 if (config != null) {
206 mcastVlan = config.egressVlan().toShort();
207 }
208
alshabib3b1eadc2016-02-01 17:57:00 -0800209 log.info("Started");
210 }
211
212 @Deactivate
213 public void deactivate() {
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800214 componentConfigService.unregisterProperties(getClass(), false);
alshabib3b1eadc2016-02-01 17:57:00 -0800215 mcastService.removeListener(listener);
ke hanf1709e82016-08-12 10:48:17 +0800216 networkConfig.removeListener(configListener);
ke han9590c812017-02-28 15:02:26 +0800217 networkConfig.unregisterConfigFactory(cordMcastConfigFactory);
218 clearGroups();
alshabib3b1eadc2016-02-01 17:57:00 -0800219 log.info("Stopped");
220 }
221
ke han9590c812017-02-28 15:02:26 +0800222 public void clearGroups() {
223 groups.keySet().forEach(d -> {
224 flowObjectiveService.next(d.getDevice(), nextObject(groups.get(d), PortNumber.ANY, NextType.Remove));
225 });
226 flowRuleService.removeFlowRulesById(appId);
227 groups.clear();
228 }
229
Jonathan Hart28271642016-02-10 16:13:54 -0800230 @Modified
231 public void modified(ComponentContext context) {
alshabibfc1cb032016-02-17 15:37:56 -0800232 Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
233
alshabibfc1cb032016-02-17 15:37:56 -0800234 try {
235 String s = get(properties, "username");
236 user = isNullOrEmpty(s) ? DEFAULT_USER : s.trim();
237
238 s = get(properties, "password");
239 password = isNullOrEmpty(s) ? DEFAULT_PASSWORD : s.trim();
240
alshabibfc1cb032016-02-17 15:37:56 -0800241 s = get(properties, "vlanEnabled");
alshabib09069c92016-02-21 14:49:51 -0800242 vlanEnabled = isNullOrEmpty(s) ? DEFAULT_VLAN_ENABLED : Boolean.parseBoolean(s.trim());
alshabibfc1cb032016-02-17 15:37:56 -0800243
244 s = get(properties, "priority");
245 priority = isNullOrEmpty(s) ? DEFAULT_PRIORITY : Integer.parseInt(s.trim());
246
Jonathan Hart0212f642016-02-20 11:32:43 -0800247 s = get(properties, "syncHost");
ke hand851dce2017-04-28 13:58:02 +0800248 syncHost = isNullOrEmpty(s) ? null : s.trim();
alshabibfc1cb032016-02-17 15:37:56 -0800249 } catch (Exception e) {
250 user = DEFAULT_USER;
251 password = DEFAULT_PASSWORD;
ke hand851dce2017-04-28 13:58:02 +0800252 syncHost = null;
alshabibfc1cb032016-02-17 15:37:56 -0800253 mcastVlan = DEFAULT_MCAST_VLAN;
254 vlanEnabled = false;
255 priority = DEFAULT_PRIORITY;
256 }
Jonathan Hart0212f642016-02-20 11:32:43 -0800257 fabricOnosUrl = createRemoteUrl(syncHost);
258 }
259
260 private static String createRemoteUrl(String remoteHost) {
261 return "http://" + remoteHost + "/onos/v1/mcast";
Jonathan Hart28271642016-02-10 16:13:54 -0800262 }
263
alshabib3b1eadc2016-02-01 17:57:00 -0800264 private class InternalMulticastListener implements McastListener {
265 @Override
266 public void event(McastEvent event) {
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800267 McastRouteInfo info = event.subject();
alshabib3b1eadc2016-02-01 17:57:00 -0800268 switch (event.type()) {
269 case ROUTE_ADDED:
270 break;
271 case ROUTE_REMOVED:
272 break;
273 case SOURCE_ADDED:
274 break;
275 case SINK_ADDED:
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800276 if (!info.sink().isPresent()) {
277 log.warn("No sink given after sink added event: {}", info);
278 return;
279 }
280 provisionGroup(info.route(), info.sink().get());
alshabib3b1eadc2016-02-01 17:57:00 -0800281 break;
282 case SINK_REMOVED:
alshabibfc1cb032016-02-17 15:37:56 -0800283 unprovisionGroup(event.subject());
alshabib3b1eadc2016-02-01 17:57:00 -0800284 break;
285 default:
286 log.warn("Unknown mcast event {}", event.type());
287 }
288 }
289 }
290
ke han9590c812017-02-28 15:02:26 +0800291 private enum NextType { AddNew, AddToExisting, Remove, RemoveFromExisting };
292
293
294 private NextObjective nextObject(Integer id, PortNumber port, NextType nextType) {
295 DefaultNextObjective.Builder build = DefaultNextObjective.builder()
296 .fromApp(appId)
297 .addTreatment(DefaultTrafficTreatment.builder().setOutput(port).build())
298 .withType(NextObjective.Type.BROADCAST)
299 .withId(id);
300 ObjectiveContext content = new ObjectiveContext() {
301 @Override
302 public void onSuccess(Objective objective) {
303 //TODO: change to debug
304 log.info("Next Objective {} installed", objective.id());
305 }
306
307 @Override
308 public void onError(Objective objective, ObjectiveError error) {
309 //TODO: change to debug
310 log.info("Next Objective {} failed, because {}",
311 objective.id(),
312 error);
313 }
314 };
315
316 switch (nextType) {
317 case AddNew:
318 return build.add(content);
319 case AddToExisting:
320 return build.addToExisting(content);
321 case Remove:
322 return build.remove(content);
323 case RemoveFromExisting:
324 return build.removeFromExisting(content);
325 default:
326 return null;
327 }
328 }
329
alshabibfc1cb032016-02-17 15:37:56 -0800330 private void unprovisionGroup(McastRouteInfo info) {
ke han9590c812017-02-28 15:02:26 +0800331
Jonathan Hart718c0452016-02-18 15:56:22 -0800332 if (info.sinks().isEmpty()) {
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800333 removeRemoteRoute(info.route());
Jonathan Hart718c0452016-02-18 15:56:22 -0800334 }
335
alshabibfc1cb032016-02-17 15:37:56 -0800336 if (!info.sink().isPresent()) {
337 log.warn("No sink given after sink removed event: {}", info);
338 return;
339 }
340 ConnectPoint loc = info.sink().get();
341
ke han9590c812017-02-28 15:02:26 +0800342 NextKey key = new NextKey(loc.deviceId(), info.route().group());
343 if (groups.get(key) == null) {
344 log.warn("No groups on device: {}", loc.deviceId());
345 return;
346 }
347 flowObjectiveService.next(loc.deviceId(), nextObject(groups.get(key), loc.port(), NextType.RemoveFromExisting));
alshabibfc1cb032016-02-17 15:37:56 -0800348 }
349
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800350 private void provisionGroup(McastRoute route, ConnectPoint sink) {
351 checkNotNull(route, "Route cannot be null");
352 checkNotNull(sink, "Sink cannot be null");
alshabib3b1eadc2016-02-01 17:57:00 -0800353
Jonathan Hart0c194962016-05-23 17:08:15 -0700354 Optional<AccessDeviceData> oltInfo = cordConfigService.getAccessDevice(sink.deviceId());
alshabib09069c92016-02-21 14:49:51 -0800355
Jonathan Hart0c194962016-05-23 17:08:15 -0700356 if (!oltInfo.isPresent()) {
alshabib09069c92016-02-21 14:49:51 -0800357 log.warn("Unknown OLT device : {}", sink.deviceId());
358 return;
359 }
360
Jonathan Hart28271642016-02-10 16:13:54 -0800361 final AtomicBoolean sync = new AtomicBoolean(false);
ke han9590c812017-02-28 15:02:26 +0800362 NextKey key = new NextKey(sink.deviceId(), route.group());
363 Integer nextId = groups.computeIfAbsent(key, (g) -> {
Zsolt Harasztiab436262016-02-25 09:39:10 -0800364 Integer id = flowObjectiveService.allocateNextId();
alshabib3b1eadc2016-02-01 17:57:00 -0800365
ke han9590c812017-02-28 15:02:26 +0800366 flowObjectiveService.next(sink.deviceId(), nextObject(id, sink.port(), NextType.AddNew));
alshabibfc1cb032016-02-17 15:37:56 -0800367
368 TrafficSelector.Builder mcast = DefaultTrafficSelector.builder()
Jonathan Hart0c194962016-05-23 17:08:15 -0700369 .matchInPort(oltInfo.get().uplink())
alshabib3b1eadc2016-02-01 17:57:00 -0800370 .matchEthType(Ethernet.TYPE_IPV4)
ke han9590c812017-02-28 15:02:26 +0800371 .matchIPDst(route.group().toIpPrefix());
alshabibfc1cb032016-02-17 15:37:56 -0800372
alshabibfc1cb032016-02-17 15:37:56 -0800373 if (vlanEnabled) {
374 mcast.matchVlanId(VlanId.vlanId((short) mcastVlan));
375 }
alshabib3b1eadc2016-02-01 17:57:00 -0800376
alshabib3b1eadc2016-02-01 17:57:00 -0800377 ForwardingObjective fwd = DefaultForwardingObjective.builder()
378 .fromApp(appId)
379 .nextStep(id)
380 .makePermanent()
381 .withFlag(ForwardingObjective.Flag.VERSATILE)
382 .withPriority(priority)
alshabibfc1cb032016-02-17 15:37:56 -0800383 .withSelector(mcast.build())
alshabib3b1eadc2016-02-01 17:57:00 -0800384 .add(new ObjectiveContext() {
385 @Override
386 public void onSuccess(Objective objective) {
387 //TODO: change to debug
388 log.info("Forwarding objective installed {}", objective);
389 }
390
391 @Override
392 public void onError(Objective objective, ObjectiveError error) {
393 //TODO: change to debug
394 log.info("Forwarding objective failed {}", objective);
395 }
396 });
397
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800398 flowObjectiveService.forward(sink.deviceId(), fwd);
alshabib3b1eadc2016-02-01 17:57:00 -0800399
Jonathan Hart28271642016-02-10 16:13:54 -0800400 sync.set(true);
401
alshabib09069c92016-02-21 14:49:51 -0800402 return id;
alshabib3b1eadc2016-02-01 17:57:00 -0800403 });
404
alshabibfc1cb032016-02-17 15:37:56 -0800405 if (!sync.get()) {
ke han9590c812017-02-28 15:02:26 +0800406 flowObjectiveService.next(sink.deviceId(), nextObject(nextId, sink.port(), NextType.AddToExisting));
alshabibfc1cb032016-02-17 15:37:56 -0800407 }
Jonathan Hart28271642016-02-10 16:13:54 -0800408
Jonathan Hart0c194962016-05-23 17:08:15 -0700409 addRemoteRoute(route, sink);
alshabib3b1eadc2016-02-01 17:57:00 -0800410 }
411
Jonathan Hart0c194962016-05-23 17:08:15 -0700412 private void addRemoteRoute(McastRoute route, ConnectPoint inPort) {
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800413 checkNotNull(route);
Jonathan Hart28271642016-02-10 16:13:54 -0800414 if (syncHost == null) {
415 log.warn("No host configured for synchronization; route will be dropped");
416 return;
417 }
418
Jonathan Hart0c194962016-05-23 17:08:15 -0700419 Optional<AccessAgentData> accessAgent = cordConfigService.getAccessAgent(inPort.deviceId());
420 if (!accessAgent.isPresent()) {
421 log.warn("No accessAgent config found for in port {}", inPort);
422 return;
423 }
424
425 if (!accessAgent.get().getOltConnectPoint(inPort).isPresent()) {
426 log.warn("No OLT configured for in port {}", inPort);
427 return;
428 }
429
430 ConnectPoint oltConnectPoint = accessAgent.get().getOltConnectPoint(inPort).get();
431
Jonathan Hart0212f642016-02-20 11:32:43 -0800432 log.debug("Sending route {} to other ONOS {}", route, fabricOnosUrl);
Jonathan Hart28271642016-02-10 16:13:54 -0800433
Jian Li46472d72016-03-09 10:52:49 -0800434 Invocation.Builder builder = getClientBuilder(fabricOnosUrl);
Jonathan Hart28271642016-02-10 16:13:54 -0800435
436 ObjectNode json = codecService.getCodec(McastRoute.class)
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800437 .encode(route, new AbstractWebResource());
Jonathan Hart0212f642016-02-20 11:32:43 -0800438
439 try {
Jian Li46472d72016-03-09 10:52:49 -0800440 builder.post(Entity.json(json.toString()));
Jonathan Hart0c194962016-05-23 17:08:15 -0700441
442 builder = getClientBuilder(fabricOnosUrl + "/sinks/" + route.group() + "/" + route.source());
443 ObjectMapper mapper = new ObjectMapper();
444 ObjectNode obj = mapper.createObjectNode();
445 obj.putArray("sinks").add(oltConnectPoint.deviceId() + "/" + oltConnectPoint.port());
446
447 builder.post(Entity.json(obj.toString()));
Jian Li46472d72016-03-09 10:52:49 -0800448 } catch (ProcessingException e) {
Jonathan Hart0212f642016-02-20 11:32:43 -0800449 log.warn("Unable to send route to remote controller: {}", e.getMessage());
450 }
alshabib3b1eadc2016-02-01 17:57:00 -0800451 }
Jonathan Hart28271642016-02-10 16:13:54 -0800452
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800453 private void removeRemoteRoute(McastRoute route) {
Jonathan Hart718c0452016-02-18 15:56:22 -0800454 if (syncHost == null) {
455 log.warn("No host configured for synchronization; route will be dropped");
456 return;
457 }
458
Jonathan Hart0212f642016-02-20 11:32:43 -0800459 log.debug("Removing route {} from other ONOS {}", route, fabricOnosUrl);
Jonathan Hart718c0452016-02-18 15:56:22 -0800460
Jian Li46472d72016-03-09 10:52:49 -0800461 Invocation.Builder builder = getClientBuilder(fabricOnosUrl)
462 .property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true);
Jonathan Hart718c0452016-02-18 15:56:22 -0800463
464 ObjectNode json = codecService.getCodec(McastRoute.class)
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800465 .encode(route, new AbstractWebResource());
Jian Li46472d72016-03-09 10:52:49 -0800466
467 builder.method("DELETE", Entity.entity(json.asText(),
468 MediaType.APPLICATION_OCTET_STREAM));
Jonathan Hart718c0452016-02-18 15:56:22 -0800469 }
470
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800471 private void clearRemoteRoutes() {
472 if (syncHost == null) {
473 log.warn("No host configured for synchronization");
474 return;
475 }
476
Jonathan Hart0212f642016-02-20 11:32:43 -0800477 log.debug("Clearing remote multicast routes from {}", fabricOnosUrl);
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800478
Jian Li46472d72016-03-09 10:52:49 -0800479 Invocation.Builder builder = getClientBuilder(fabricOnosUrl);
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800480 List<McastRoute> mcastRoutes = Lists.newArrayList();
Jonathan Hart0212f642016-02-20 11:32:43 -0800481
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800482 try {
Jonathan Hart0212f642016-02-20 11:32:43 -0800483 String response = builder
484 .accept(MediaType.APPLICATION_JSON_TYPE)
485 .get(String.class);
486
487 JsonCodec<McastRoute> routeCodec = codecService.getCodec(McastRoute.class);
488 ObjectMapper mapper = new ObjectMapper();
489
490
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800491 ObjectNode node = (ObjectNode) mapper.readTree(response);
492 ArrayNode list = (ArrayNode) node.path("routes");
493
494 list.forEach(n -> mcastRoutes.add(
495 routeCodec.decode((ObjectNode) n, new AbstractWebResource())));
Jonathan Hart0212f642016-02-20 11:32:43 -0800496
Jonathan Hart0c194962016-05-23 17:08:15 -0700497 } catch (IOException | ProcessingException e) {
Jonathan Hartc3f84eb2016-02-19 12:44:36 -0800498 log.warn("Error clearing remote routes", e);
499 }
500
501 mcastRoutes.forEach(this::removeRemoteRoute);
502 }
503
Jian Li46472d72016-03-09 10:52:49 -0800504 private Invocation.Builder getClientBuilder(String uri) {
505 ClientConfig config = new ClientConfig();
506 Client client = ClientBuilder.newClient(config);
507
508 client.property(ClientProperties.CONNECT_TIMEOUT, DEFAULT_REST_TIMEOUT_MS);
509 client.property(ClientProperties.READ_TIMEOUT, DEFAULT_REST_TIMEOUT_MS);
510 client.register(HttpAuthenticationFeature.basic(user, password));
511
512 WebTarget wt = client.target(uri);
513 return wt.request(JSON_UTF_8.toString());
Jonathan Hart28271642016-02-10 16:13:54 -0800514 }
515
ke hanf1709e82016-08-12 10:48:17 +0800516 private class InternalNetworkConfigListener implements NetworkConfigListener {
517 @Override
518 public void event(NetworkConfigEvent event) {
519 switch (event.type()) {
520
521 case CONFIG_ADDED:
522 case CONFIG_UPDATED:
523 if (event.configClass().equals(CORD_MCAST_CONFIG_CLASS)) {
524 McastConfig config = networkConfig.getConfig(coreAppId, CORD_MCAST_CONFIG_CLASS);
525 if (config != null) {
ke han9590c812017-02-28 15:02:26 +0800526 //TODO: Simply remove flows/groups, hosts will response period query
527 // and re-sent IGMP report, so the flows can be rebuild.
528 // However, better to remove and re-add mcast flow rules here
529 if (mcastVlan != config.egressVlan().toShort() && vlanEnabled) {
530 clearGroups();
531 }
ke hanf1709e82016-08-12 10:48:17 +0800532 mcastVlan = config.egressVlan().toShort();
533 }
534 }
535 break;
536 case CONFIG_REGISTERED:
537 case CONFIG_UNREGISTERED:
538 case CONFIG_REMOVED:
539 break;
540 default:
541 break;
542 }
543 }
544 }
ke han9590c812017-02-28 15:02:26 +0800545
546 private class NextKey {
547 private DeviceId device;
548 private IpAddress group;
549 public NextKey(DeviceId deviceId, IpAddress groupAddress) {
550 device = deviceId;
551 group = groupAddress;
552 }
553
554 public DeviceId getDevice() {
555 return device;
556 }
557
558 public int hashCode() {
559 return com.google.common.base.Objects.hashCode(new Object[]{this.device, this.group});
560 }
561
562 public boolean equals(Object obj) {
563 if (this == obj) {
564 return true;
565 } else if (!(obj instanceof NextKey)) {
566 return false;
567 } else {
568 NextKey that = (NextKey) obj;
569 return this.getClass() == that.getClass() &&
570 Objects.equals(this.device, that.device) &&
571 Objects.equals(this.group, that.group);
572 }
573 }
574 }
alshabib3b1eadc2016-02-01 17:57:00 -0800575}
ke hanf1709e82016-08-12 10:48:17 +0800576
ke han9590c812017-02-28 15:02:26 +0800577