blob: b0392f450cf4eea9e96049e2cf20cd67cffe834e [file] [log] [blame]
alshabib3b1eadc2016-02-01 17:57:00 -08001/*
2 * Copyright 2015-2016 Open Networking Laboratory
3 *
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 */
16package org.onosproject.cordmcast;
17
Jonathan Hart28271642016-02-10 16:13:54 -080018import com.fasterxml.jackson.databind.node.ObjectNode;
alshabib3b1eadc2016-02-01 17:57:00 -080019import com.google.common.collect.Maps;
Jonathan Hart28271642016-02-10 16:13:54 -080020import com.sun.jersey.api.client.Client;
21import com.sun.jersey.api.client.WebResource;
22import com.sun.jersey.api.client.filter.HTTPBasicAuthFilter;
alshabib3b1eadc2016-02-01 17:57:00 -080023import org.apache.felix.scr.annotations.Activate;
24import org.apache.felix.scr.annotations.Component;
25import org.apache.felix.scr.annotations.Deactivate;
Jonathan Hart28271642016-02-10 16:13:54 -080026import org.apache.felix.scr.annotations.Modified;
27import org.apache.felix.scr.annotations.Property;
alshabib3b1eadc2016-02-01 17:57:00 -080028import org.apache.felix.scr.annotations.Reference;
29import org.apache.felix.scr.annotations.ReferenceCardinality;
30import org.onlab.packet.Ethernet;
31import org.onlab.packet.IPv4;
32import org.onlab.packet.IpAddress;
33import org.onlab.packet.VlanId;
Jonathan Hart28271642016-02-10 16:13:54 -080034import org.onlab.util.Tools;
35import org.onosproject.cfg.ComponentConfigService;
36import org.onosproject.codec.CodecService;
alshabib3b1eadc2016-02-01 17:57:00 -080037import org.onosproject.core.ApplicationId;
38import org.onosproject.core.CoreService;
39import org.onosproject.net.ConnectPoint;
40import org.onosproject.net.flow.DefaultTrafficSelector;
41import org.onosproject.net.flow.DefaultTrafficTreatment;
42import org.onosproject.net.flow.TrafficSelector;
43import org.onosproject.net.flowobjective.DefaultForwardingObjective;
44import org.onosproject.net.flowobjective.DefaultNextObjective;
45import org.onosproject.net.flowobjective.FlowObjectiveService;
46import org.onosproject.net.flowobjective.ForwardingObjective;
47import org.onosproject.net.flowobjective.NextObjective;
48import org.onosproject.net.flowobjective.Objective;
49import org.onosproject.net.flowobjective.ObjectiveContext;
50import org.onosproject.net.flowobjective.ObjectiveError;
51import org.onosproject.net.group.GroupService;
52import org.onosproject.net.mcast.McastEvent;
53import org.onosproject.net.mcast.McastListener;
Jonathan Hart28271642016-02-10 16:13:54 -080054import org.onosproject.net.mcast.McastRoute;
alshabib3b1eadc2016-02-01 17:57:00 -080055import org.onosproject.net.mcast.McastRouteInfo;
56import org.onosproject.net.mcast.MulticastRouteService;
Jonathan Hart28271642016-02-10 16:13:54 -080057import org.onosproject.rest.AbstractWebResource;
58import org.osgi.service.component.ComponentContext;
alshabib3b1eadc2016-02-01 17:57:00 -080059import org.slf4j.Logger;
60
Jonathan Hart28271642016-02-10 16:13:54 -080061import java.util.Dictionary;
alshabib3b1eadc2016-02-01 17:57:00 -080062import java.util.Map;
Jonathan Hart28271642016-02-10 16:13:54 -080063import java.util.concurrent.atomic.AtomicBoolean;
alshabib3b1eadc2016-02-01 17:57:00 -080064import java.util.concurrent.atomic.AtomicInteger;
65
Jonathan Hart28271642016-02-10 16:13:54 -080066import static com.google.common.net.MediaType.JSON_UTF_8;
alshabib3b1eadc2016-02-01 17:57:00 -080067import static org.slf4j.LoggerFactory.getLogger;
68
69/**
70 * CORD multicast provisoning application. Operates by listening to
Jonathan Hart28271642016-02-10 16:13:54 -080071 * events on the multicast rib and provisioning groups to program multicast
alshabib3b1eadc2016-02-01 17:57:00 -080072 * flows on the dataplane.
73 */
74@Component(immediate = true)
75public class CordMcast {
76
77 private static final int DEFAULT_PRIORITY = 1000;
78 private static final short DEFAULT_MCAST_VLAN = 4000;
79 private final Logger log = getLogger(getClass());
80
81 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
82 protected MulticastRouteService mcastService;
83
84 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
85 protected GroupService groupService;
86
87 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
88 protected FlowObjectiveService flowObjectiveService;
89
90 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
91 protected CoreService coreService;
92
Jonathan Hart28271642016-02-10 16:13:54 -080093 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
94 protected CodecService codecService;
95
96 @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
97 protected ComponentConfigService componentConfigService;
98
99
alshabib3b1eadc2016-02-01 17:57:00 -0800100 protected McastListener listener = new InternalMulticastListener();
101
102
103
104 //TODO: move this to a ec map
105 private Map<IpAddress, Integer> groups = Maps.newConcurrentMap();
106
107 //TODO: move this to distributed atomic long
108 private AtomicInteger channels = new AtomicInteger(0);
109
110 private ApplicationId appId;
111
112 //TODO: network config this
113 private short mcastVlan = DEFAULT_MCAST_VLAN;
114
115 // TODO component config this
116 private int priority = DEFAULT_PRIORITY;
117
Jonathan Hart28271642016-02-10 16:13:54 -0800118 private static final String DEFAULT_USER = "karaf";
119 private static final String DEFAULT_PASSWORD = "karaf";
120
121 @Property(name = "syncHost", value = "",
122 label = "host:port to synchronize routes to")
123 private String syncHost = "10.90.0.8:8181";
124
125 @Property(name = "username", value = DEFAULT_USER,
126 label = "Username for REST password authentication")
127 private String user = DEFAULT_USER;
128
129 @Property(name = "password", value = DEFAULT_PASSWORD,
130 label = "Password for REST authentication")
131 private String password = DEFAULT_PASSWORD;
132
133 private String fabricOnosUrl;
134
alshabib3b1eadc2016-02-01 17:57:00 -0800135 @Activate
136 public void activate() {
137 appId = coreService.registerApplication("org.onosproject.cordmcast");
Jonathan Hart28271642016-02-10 16:13:54 -0800138 componentConfigService.registerProperties(getClass());
alshabib3b1eadc2016-02-01 17:57:00 -0800139 mcastService.addListener(listener);
Jonathan Hart28271642016-02-10 16:13:54 -0800140
141 fabricOnosUrl = "http://" + syncHost + "/onos/v1/mcast";
142
alshabib3b1eadc2016-02-01 17:57:00 -0800143 //TODO: obtain all existing mcast routes
144 log.info("Started");
145 }
146
147 @Deactivate
148 public void deactivate() {
Jonathan Hart28271642016-02-10 16:13:54 -0800149 componentConfigService.unregisterProperties(getClass(), true);
alshabib3b1eadc2016-02-01 17:57:00 -0800150 mcastService.removeListener(listener);
151 log.info("Stopped");
152 }
153
Jonathan Hart28271642016-02-10 16:13:54 -0800154 @Modified
155 public void modified(ComponentContext context) {
156 Dictionary<?, ?> properties = context.getProperties();
157 user = Tools.get(properties, "username");
158 password = Tools.get(properties, "password");
159 syncHost = Tools.get(properties, "syncHost");
160 }
161
alshabib3b1eadc2016-02-01 17:57:00 -0800162 private class InternalMulticastListener implements McastListener {
163 @Override
164 public void event(McastEvent event) {
165 switch (event.type()) {
166 case ROUTE_ADDED:
167 break;
168 case ROUTE_REMOVED:
169 break;
170 case SOURCE_ADDED:
171 break;
172 case SINK_ADDED:
173 provisionGroup(event.subject());
174 break;
175 case SINK_REMOVED:
176 break;
177 default:
178 log.warn("Unknown mcast event {}", event.type());
179 }
180 }
181 }
182
183 private void provisionGroup(McastRouteInfo info) {
184 if (!info.sink().isPresent()) {
185 log.warn("No sink given after sink added event: {}", info);
186 return;
187 }
188 ConnectPoint loc = info.sink().get();
189
Jonathan Hart28271642016-02-10 16:13:54 -0800190 final AtomicBoolean sync = new AtomicBoolean(false);
alshabib3b1eadc2016-02-01 17:57:00 -0800191
192 Integer nextId = groups.computeIfAbsent(info.route().group(), (g) -> {
Jonathan Hart28271642016-02-10 16:13:54 -0800193 Integer id = allocateId();
alshabib3b1eadc2016-02-01 17:57:00 -0800194
195 TrafficSelector mcast = DefaultTrafficSelector.builder()
196 .matchVlanId(VlanId.vlanId(mcastVlan))
197 .matchEthType(Ethernet.TYPE_IPV4)
198 .matchIPProtocol(IPv4.PROTOCOL_IGMP)
199 .matchIPDst(g.toIpPrefix())
200 .build();
201
202
203 ForwardingObjective fwd = DefaultForwardingObjective.builder()
204 .fromApp(appId)
205 .nextStep(id)
206 .makePermanent()
207 .withFlag(ForwardingObjective.Flag.VERSATILE)
208 .withPriority(priority)
209 .withSelector(mcast)
210 .add(new ObjectiveContext() {
211 @Override
212 public void onSuccess(Objective objective) {
213 //TODO: change to debug
214 log.info("Forwarding objective installed {}", objective);
215 }
216
217 @Override
218 public void onError(Objective objective, ObjectiveError error) {
219 //TODO: change to debug
220 log.info("Forwarding objective failed {}", objective);
221 }
222 });
223
224 flowObjectiveService.forward(loc.deviceId(), fwd);
225
Jonathan Hart28271642016-02-10 16:13:54 -0800226 sync.set(true);
227
alshabib3b1eadc2016-02-01 17:57:00 -0800228 return id;
229 });
230
231 NextObjective next = DefaultNextObjective.builder()
232 .fromApp(appId)
233 .addTreatment(DefaultTrafficTreatment.builder().setOutput(loc.port()).build())
234 .withType(NextObjective.Type.BROADCAST)
235 .withId(nextId)
236 .addToExisting(new ObjectiveContext() {
237 @Override
238 public void onSuccess(Objective objective) {
239 //TODO: change to debug
240 log.info("Next Objective {} installed", objective.id());
241 }
242
243 @Override
244 public void onError(Objective objective, ObjectiveError error) {
245 //TODO: change to debug
246 log.info("Next Objective {} failed, because {}",
247 objective.id(),
248 error);
249 }
250 });
251
252 flowObjectiveService.next(loc.deviceId(), next);
Jonathan Hart28271642016-02-10 16:13:54 -0800253
254 if (sync.get()) {
255 syncRoute(info);
256 }
alshabib3b1eadc2016-02-01 17:57:00 -0800257 }
258
Jonathan Hart28271642016-02-10 16:13:54 -0800259 private void syncRoute(McastRouteInfo info) {
260 if (syncHost == null) {
261 log.warn("No host configured for synchronization; route will be dropped");
262 return;
263 }
264
265 log.debug("Sending route to other ONOS: {}", info.route());
266
267 WebResource.Builder builder = getClientBuilder(fabricOnosUrl);
268
269 ObjectNode json = codecService.getCodec(McastRoute.class)
270 .encode(info.route(), new AbstractWebResource());
271 builder.post(json.toString());
alshabib3b1eadc2016-02-01 17:57:00 -0800272 }
Jonathan Hart28271642016-02-10 16:13:54 -0800273
274 private Integer allocateId() {
275 return channels.getAndIncrement();
276 }
277
278 private WebResource.Builder getClientBuilder(String uri) {
279 Client client = Client.create();
280 client.addFilter(new HTTPBasicAuthFilter(user, password));
281 WebResource resource = client.resource(uri);
282 return resource.accept(JSON_UTF_8.toString())
283 .type(JSON_UTF_8.toString());
284 }
285
alshabib3b1eadc2016-02-01 17:57:00 -0800286}