blob: e4b50a10c20e4122d196dfc17e7d5cb05e67b4d4 [file] [log] [blame]
alshabib0ccde6d2015-05-30 18:22:36 -07001/*
2 * Copyright 2015 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.driver.pipeline;
17
alshabibfd430b62015-12-16 18:56:38 -080018import com.google.common.collect.Lists;
19import org.apache.commons.lang3.tuple.ImmutablePair;
20import org.apache.commons.lang3.tuple.Pair;
alshabib0ccde6d2015-05-30 18:22:36 -070021import org.onlab.osgi.ServiceDirectory;
Jonathan Hartdfc3b862015-07-01 14:49:56 -070022import org.onlab.packet.EthType;
alshabibfd430b62015-12-16 18:56:38 -080023import org.onlab.packet.IPv4;
24import org.onlab.packet.VlanId;
Jonathan Hartdfc3b862015-07-01 14:49:56 -070025import org.onosproject.core.ApplicationId;
26import org.onosproject.core.CoreService;
alshabib2cc73cb2015-06-30 20:26:56 -070027import org.onosproject.net.DefaultAnnotations;
28import org.onosproject.net.Device;
alshabib0ccde6d2015-05-30 18:22:36 -070029import org.onosproject.net.DeviceId;
alshabib2cc73cb2015-06-30 20:26:56 -070030import org.onosproject.net.MastershipRole;
alshabib0ccde6d2015-05-30 18:22:36 -070031import org.onosproject.net.PortNumber;
32import org.onosproject.net.behaviour.Pipeliner;
33import org.onosproject.net.behaviour.PipelinerContext;
alshabib2cc73cb2015-06-30 20:26:56 -070034import org.onosproject.net.device.DefaultDeviceDescription;
35import org.onosproject.net.device.DeviceDescription;
36import org.onosproject.net.device.DeviceProvider;
37import org.onosproject.net.device.DeviceProviderRegistry;
alshabib2cc73cb2015-06-30 20:26:56 -070038import org.onosproject.net.device.DeviceService;
alshabib0ccde6d2015-05-30 18:22:36 -070039import org.onosproject.net.driver.AbstractHandlerBehaviour;
40import org.onosproject.net.flow.DefaultFlowRule;
Jonathan Hartdfc3b862015-07-01 14:49:56 -070041import org.onosproject.net.flow.DefaultTrafficSelector;
alshabib0ccde6d2015-05-30 18:22:36 -070042import org.onosproject.net.flow.DefaultTrafficTreatment;
43import org.onosproject.net.flow.FlowRule;
44import org.onosproject.net.flow.FlowRuleOperations;
45import org.onosproject.net.flow.FlowRuleOperationsContext;
46import org.onosproject.net.flow.FlowRuleService;
47import org.onosproject.net.flow.TrafficSelector;
48import org.onosproject.net.flow.TrafficTreatment;
alshabibfd430b62015-12-16 18:56:38 -080049import org.onosproject.net.flow.criteria.Criteria;
50import org.onosproject.net.flow.criteria.Criterion;
51import org.onosproject.net.flow.criteria.EthTypeCriterion;
52import org.onosproject.net.flow.criteria.IPProtocolCriterion;
53import org.onosproject.net.flow.criteria.PortCriterion;
alshabib2f74f2c2016-01-14 13:29:35 -080054import org.onosproject.net.flow.criteria.VlanIdCriterion;
alshabibfd430b62015-12-16 18:56:38 -080055import org.onosproject.net.flow.instructions.Instruction;
alshabib0ccde6d2015-05-30 18:22:36 -070056import org.onosproject.net.flow.instructions.Instructions;
alshabibfd430b62015-12-16 18:56:38 -080057import org.onosproject.net.flow.instructions.L2ModificationInstruction;
alshabib0ccde6d2015-05-30 18:22:36 -070058import org.onosproject.net.flowobjective.FilteringObjective;
59import org.onosproject.net.flowobjective.ForwardingObjective;
60import org.onosproject.net.flowobjective.NextObjective;
alshabibfd430b62015-12-16 18:56:38 -080061import org.onosproject.net.flowobjective.Objective;
alshabib0ccde6d2015-05-30 18:22:36 -070062import org.onosproject.net.flowobjective.ObjectiveError;
alshabib2cc73cb2015-06-30 20:26:56 -070063import org.onosproject.net.provider.AbstractProvider;
64import org.onosproject.net.provider.ProviderId;
alshabib0ccde6d2015-05-30 18:22:36 -070065import org.slf4j.Logger;
66
alshabibfd430b62015-12-16 18:56:38 -080067import java.util.Collection;
68import java.util.List;
69import java.util.Optional;
70import java.util.stream.Collectors;
71
alshabib2cc73cb2015-06-30 20:26:56 -070072import static com.google.common.base.Preconditions.checkNotNull;
alshabib0ccde6d2015-05-30 18:22:36 -070073import static org.slf4j.LoggerFactory.getLogger;
74
75/**
Jonathan Hart64da69d2015-07-15 15:10:28 -070076 * Pipeliner for OLT device.
alshabib0ccde6d2015-05-30 18:22:36 -070077 */
alshabibfd430b62015-12-16 18:56:38 -080078
Jonathan Hartb92cc512015-11-16 23:05:21 -080079public class OltPipeline extends AbstractHandlerBehaviour implements Pipeliner {
alshabib0ccde6d2015-05-30 18:22:36 -070080
alshabibfd430b62015-12-16 18:56:38 -080081 private static final Integer QQ_TABLE = 1;
alshabib0ccde6d2015-05-30 18:22:36 -070082 private final Logger log = getLogger(getClass());
83
alshabib2cc73cb2015-06-30 20:26:56 -070084 static final ProviderId PID = new ProviderId("olt", "org.onosproject.olt", true);
85
86 static final String DEVICE = "isAccess";
87 static final String OLT = "true";
88
alshabib0ccde6d2015-05-30 18:22:36 -070089 private ServiceDirectory serviceDirectory;
90 private FlowRuleService flowRuleService;
91 private DeviceId deviceId;
Jonathan Hartdfc3b862015-07-01 14:49:56 -070092 private CoreService coreService;
93
94 private ApplicationId appId;
alshabib0ccde6d2015-05-30 18:22:36 -070095
alshabib2cc73cb2015-06-30 20:26:56 -070096 private DeviceProvider provider = new AnnotationProvider();
97
alshabib0ccde6d2015-05-30 18:22:36 -070098 @Override
99 public void init(DeviceId deviceId, PipelinerContext context) {
alshabibfd430b62015-12-16 18:56:38 -0800100 log.debug("Initiate OLT pipeline");
alshabib0ccde6d2015-05-30 18:22:36 -0700101 this.serviceDirectory = context.directory();
102 this.deviceId = deviceId;
alshabib2cc73cb2015-06-30 20:26:56 -0700103 DeviceProviderRegistry registry =
alshabibfd430b62015-12-16 18:56:38 -0800104 serviceDirectory.get(DeviceProviderRegistry.class);
alshabib0ccde6d2015-05-30 18:22:36 -0700105 flowRuleService = serviceDirectory.get(FlowRuleService.class);
Jonathan Hartdfc3b862015-07-01 14:49:56 -0700106 coreService = serviceDirectory.get(CoreService.class);
alshabibb32cefe2015-06-08 18:15:05 -0700107
Jonathan Hartdfc3b862015-07-01 14:49:56 -0700108 appId = coreService.registerApplication(
109 "org.onosproject.driver.OLTPipeline");
110
alshabibb32cefe2015-06-08 18:15:05 -0700111 }
112
alshabib0ccde6d2015-05-30 18:22:36 -0700113 @Override
114 public void filter(FilteringObjective filter) {
alshabibfd430b62015-12-16 18:56:38 -0800115 Instructions.OutputInstruction output;
alshabib0ccde6d2015-05-30 18:22:36 -0700116
alshabibfd430b62015-12-16 18:56:38 -0800117 if (filter.meta() != null && !filter.meta().immediate().isEmpty()) {
118 output = (Instructions.OutputInstruction) filter.meta().immediate().stream()
119 .filter(t -> t.type().equals(Instruction.Type.OUTPUT))
120 .limit(1)
121 .findFirst().get();
alshabib0ccde6d2015-05-30 18:22:36 -0700122
alshabibfd430b62015-12-16 18:56:38 -0800123 if (output != null && !output.port().equals(PortNumber.CONTROLLER)) {
124 fail(filter, ObjectiveError.UNSUPPORTED);
125 return;
alshabib0ccde6d2015-05-30 18:22:36 -0700126 }
alshabibfd430b62015-12-16 18:56:38 -0800127 } else {
128 fail(filter, ObjectiveError.BADPARAMS);
alshabib0ccde6d2015-05-30 18:22:36 -0700129 return;
130 }
131
alshabibfd430b62015-12-16 18:56:38 -0800132 if (filter.key().type() != Criterion.Type.IN_PORT) {
133 fail(filter, ObjectiveError.BADPARAMS);
134 return;
135 }
136
137 EthTypeCriterion ethType = (EthTypeCriterion)
138 filterForCriterion(filter.conditions(), Criterion.Type.ETH_TYPE);
139
140 if (ethType == null) {
141 fail(filter, ObjectiveError.BADPARAMS);
142 return;
143 }
144
145 if (ethType.ethType().equals(EthType.EtherType.EAPOL)) {
146 provisionEapol(filter, ethType, output);
147 } else if (ethType.ethType().equals(EthType.EtherType.IPV4)) {
148 IPProtocolCriterion ipProto = (IPProtocolCriterion)
149 filterForCriterion(filter.conditions(), Criterion.Type.IP_PROTO);
150 if (ipProto.protocol() == IPv4.PROTOCOL_IGMP) {
151 provisionIGMP(filter, ethType, ipProto, output);
152 }
153 } else {
154 fail(filter, ObjectiveError.UNSUPPORTED);
155 }
156
157 }
158
159
160 @Override
161 public void forward(ForwardingObjective fwd) {
alshabib0ccde6d2015-05-30 18:22:36 -0700162 TrafficTreatment treatment = fwd.treatment();
alshabib0ccde6d2015-05-30 18:22:36 -0700163
alshabibfd430b62015-12-16 18:56:38 -0800164 List<Instruction> instructions = treatment.allInstructions();
alshabib0ccde6d2015-05-30 18:22:36 -0700165
alshabibfd430b62015-12-16 18:56:38 -0800166 Optional<Instruction> vlanIntruction = instructions.stream()
167 .filter(i -> i.type() == Instruction.Type.L2MODIFICATION)
168 .filter(i -> ((L2ModificationInstruction) i).subtype() ==
169 L2ModificationInstruction.L2SubType.VLAN_PUSH ||
170 ((L2ModificationInstruction) i).subtype() ==
171 L2ModificationInstruction.L2SubType.VLAN_POP)
172 .findAny();
173
174 if (!vlanIntruction.isPresent()) {
175 fail(fwd, ObjectiveError.BADPARAMS);
176 return;
177 }
178
179 L2ModificationInstruction vlanIns =
180 (L2ModificationInstruction) vlanIntruction.get();
181
182 if (vlanIns.subtype() == L2ModificationInstruction.L2SubType.VLAN_PUSH) {
183 installUpstreamRules(fwd);
184 } else if (vlanIns.subtype() == L2ModificationInstruction.L2SubType.VLAN_POP) {
185 installDownstreamRules(fwd);
alshabib0ccde6d2015-05-30 18:22:36 -0700186 } else {
alshabibfd430b62015-12-16 18:56:38 -0800187 log.error("Unknown OLT operation: {}", fwd);
188 fail(fwd, ObjectiveError.UNSUPPORTED);
189 return;
alshabib0ccde6d2015-05-30 18:22:36 -0700190 }
191
alshabibfd430b62015-12-16 18:56:38 -0800192 pass(fwd);
alshabib0ccde6d2015-05-30 18:22:36 -0700193
alshabib0ccde6d2015-05-30 18:22:36 -0700194 }
195
196 @Override
197 public void next(NextObjective nextObjective) {
Jonathan Hart3e594642015-10-20 17:31:24 -0700198 throw new UnsupportedOperationException("OLT does not next hop.");
alshabib0ccde6d2015-05-30 18:22:36 -0700199 }
200
alshabibfd430b62015-12-16 18:56:38 -0800201 private void installDownstreamRules(ForwardingObjective fwd) {
202 List<Pair<Instruction, Instruction>> vlanOps =
203 vlanOps(fwd,
204 L2ModificationInstruction.L2SubType.VLAN_POP);
205
206 if (vlanOps == null) {
207 return;
208 }
209
210 Instruction output = fetchOutput(fwd, "downstream");
211
212 if (output == null) {
213 return;
214 }
215
216 Pair<Instruction, Instruction> popAndRewrite = vlanOps.remove(0);
217
alshabibfa0dc662016-01-13 11:23:53 -0800218 TrafficSelector selector = fwd.selector();
219
220 Criterion outerVlan = selector.getCriterion(Criterion.Type.VLAN_VID);
221 Criterion innerVlan = selector.getCriterion(Criterion.Type.INNER_VLAN_VID);
222 Criterion inport = selector.getCriterion(Criterion.Type.IN_PORT);
223
224 if (outerVlan == null || innerVlan == null || inport == null) {
225 log.error("Forwarding objective is underspecified: {}", fwd);
226 fail(fwd, ObjectiveError.BADPARAMS);
227 return;
228 }
229
alshabib2f74f2c2016-01-14 13:29:35 -0800230 Criterion innerVid = Criteria.matchVlanId(((VlanIdCriterion) innerVlan).vlanId());
231
alshabibfa0dc662016-01-13 11:23:53 -0800232 FlowRule.Builder outer = DefaultFlowRule.builder()
alshabibfd430b62015-12-16 18:56:38 -0800233 .forDevice(deviceId)
234 .fromApp(appId)
235 .makePermanent()
236 .withPriority(fwd.priority())
alshabibfa0dc662016-01-13 11:23:53 -0800237 .withSelector(buildSelector(inport, outerVlan))
alshabibfd430b62015-12-16 18:56:38 -0800238 .withTreatment(buildTreatment(popAndRewrite.getLeft(),
239 Instructions.transition(QQ_TABLE)));
alshabibfd430b62015-12-16 18:56:38 -0800240
alshabibfa0dc662016-01-13 11:23:53 -0800241 FlowRule.Builder inner = DefaultFlowRule.builder()
alshabibfd430b62015-12-16 18:56:38 -0800242 .forDevice(deviceId)
243 .fromApp(appId)
244 .forTable(QQ_TABLE)
245 .makePermanent()
246 .withPriority(fwd.priority())
alshabib2f74f2c2016-01-14 13:29:35 -0800247 .withSelector(buildSelector(inport, innerVid))
alshabibfd430b62015-12-16 18:56:38 -0800248 .withTreatment(buildTreatment(popAndRewrite.getRight(),
249 output));
250
251 applyRules(fwd, inner, outer);
252
253 }
254
255 private void installUpstreamRules(ForwardingObjective fwd) {
256 List<Pair<Instruction, Instruction>> vlanOps =
257 vlanOps(fwd,
258 L2ModificationInstruction.L2SubType.VLAN_PUSH);
259
260 if (vlanOps == null) {
261 return;
262 }
263
264 Instruction output = fetchOutput(fwd, "upstream");
265
266 if (output == null) {
267 return;
268 }
269
270 Pair<Instruction, Instruction> innerPair = vlanOps.remove(0);
271
272 Pair<Instruction, Instruction> outerPair = vlanOps.remove(0);
273
274 FlowRule.Builder inner = DefaultFlowRule.builder()
275 .forDevice(deviceId)
276 .fromApp(appId)
277 .makePermanent()
278 .withPriority(fwd.priority())
279 .withSelector(fwd.selector())
280 .withTreatment(buildTreatment(innerPair.getRight(),
281 Instructions.transition(QQ_TABLE)));
282
283 PortCriterion inPort = (PortCriterion)
284 fwd.selector().getCriterion(Criterion.Type.IN_PORT);
285
286 VlanId cVlanId = ((L2ModificationInstruction.ModVlanIdInstruction)
287 innerPair.getRight()).vlanId();
288
289 FlowRule.Builder outer = DefaultFlowRule.builder()
290 .forDevice(deviceId)
291 .fromApp(appId)
292 .forTable(QQ_TABLE)
293 .makePermanent()
294 .withPriority(fwd.priority())
295 .withSelector(buildSelector(inPort,
296 Criteria.matchVlanId(cVlanId)))
297 .withTreatment(buildTreatment(outerPair.getLeft(),
298 outerPair.getRight(),
299 output));
300
301 applyRules(fwd, inner, outer);
302
303 }
304
305 private Instruction fetchOutput(ForwardingObjective fwd, String direction) {
306 Instruction output = fwd.treatment().allInstructions().stream()
307 .filter(i -> i.type() == Instruction.Type.OUTPUT)
308 .findFirst().orElse(null);
309
310 if (output == null) {
311 log.error("OLT {} rule has no output", direction);
312 fail(fwd, ObjectiveError.BADPARAMS);
313 return null;
314 }
315 return output;
316 }
317
318 private List<Pair<Instruction, Instruction>> vlanOps(ForwardingObjective fwd,
319 L2ModificationInstruction.L2SubType type) {
320
321 List<Pair<Instruction, Instruction>> vlanOps = findVlanOps(
322 fwd.treatment().allInstructions(), type);
323
324 if (vlanOps == null) {
325 String direction = type == L2ModificationInstruction.L2SubType.VLAN_POP
326 ? "downstream" : "upstream";
327 log.error("Missing vlan operations in {} forwarding: {}", direction, fwd);
328 fail(fwd, ObjectiveError.BADPARAMS);
329 return null;
330 }
331 return vlanOps;
332 }
333
334
335 private List<Pair<Instruction, Instruction>> findVlanOps(List<Instruction> instructions,
336 L2ModificationInstruction.L2SubType type) {
337
338 List<Instruction> vlanPushs = findL2Instructions(
339 type,
340 instructions);
341 List<Instruction> vlanSets = findL2Instructions(
342 L2ModificationInstruction.L2SubType.VLAN_ID,
343 instructions);
344
345 if (vlanPushs.size() != vlanSets.size()) {
346 return null;
347 }
348
349 List<Pair<Instruction, Instruction>> pairs = Lists.newArrayList();
350
351 for (int i = 0; i < vlanPushs.size(); i++) {
352 pairs.add(new ImmutablePair<>(vlanPushs.get(i), vlanSets.get(i)));
353 }
354 return pairs;
355 }
356
357 private List<Instruction> findL2Instructions(L2ModificationInstruction.L2SubType subType,
358 List<Instruction> actions) {
359 return actions.stream()
360 .filter(i -> i.type() == Instruction.Type.L2MODIFICATION)
361 .filter(i -> ((L2ModificationInstruction) i).subtype() == subType)
362 .collect(Collectors.toList());
363 }
364
365 private void provisionEapol(FilteringObjective filter,
366 EthTypeCriterion ethType,
367 Instructions.OutputInstruction output) {
368
369 TrafficSelector selector = buildSelector(filter.key(), ethType);
370 TrafficTreatment treatment = buildTreatment(output);
371 buildAndApplyRule(filter, selector, treatment);
372
373 }
374
375 private void provisionIGMP(FilteringObjective filter, EthTypeCriterion ethType,
376 IPProtocolCriterion ipProto,
377 Instructions.OutputInstruction output) {
378 TrafficSelector selector = buildSelector(filter.key(), ethType, ipProto);
379 TrafficTreatment treatment = buildTreatment(output);
380 buildAndApplyRule(filter, selector, treatment);
381 }
382
383 private void buildAndApplyRule(FilteringObjective filter, TrafficSelector selector,
384 TrafficTreatment treatment) {
385 FlowRule rule = DefaultFlowRule.builder()
386 .forDevice(deviceId)
387 .forTable(0)
388 .fromApp(filter.appId())
389 .makePermanent()
390 .withSelector(selector)
391 .withTreatment(treatment)
392 .build();
393
394 FlowRuleOperations.Builder opsBuilder = FlowRuleOperations.builder();
395
396 switch (filter.type()) {
397 case PERMIT:
398 opsBuilder.add(rule);
399 break;
400 case DENY:
401 opsBuilder.remove(rule);
402 break;
403 default:
404 log.warn("Unknown filter type : {}", filter.type());
405 fail(filter, ObjectiveError.UNSUPPORTED);
406 }
407
408 applyFlowRules(opsBuilder, filter);
409 }
410
411 private void applyRules(ForwardingObjective fwd,
412 FlowRule.Builder inner, FlowRule.Builder outer) {
413 FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
414 switch (fwd.op()) {
415 case ADD:
416 builder.add(inner.build()).add(outer.build());
417 break;
418 case REMOVE:
419 builder.remove(inner.build()).remove(outer.build());
420 break;
421 case ADD_TO_EXISTING:
422 break;
423 case REMOVE_FROM_EXISTING:
424 break;
425 default:
426 log.warn("Unknown forwarding operation: {}", fwd.op());
427 }
428
429 applyFlowRules(builder, fwd);
430 }
431
432 private void applyFlowRules(FlowRuleOperations.Builder builder,
433 Objective objective) {
434 flowRuleService.apply(builder.build(new FlowRuleOperationsContext() {
435 @Override
436 public void onSuccess(FlowRuleOperations ops) {
437 pass(objective);
438 }
439
440 @Override
441 public void onError(FlowRuleOperations ops) {
442 fail(objective, ObjectiveError.FLOWINSTALLATIONFAILED);
443 }
444 }));
445 }
446
447 private Criterion filterForCriterion(Collection<Criterion> criteria, Criterion.Type type) {
448 return criteria.stream()
449 .filter(c -> c.type().equals(Criterion.Type.ETH_TYPE))
450 .limit(1)
451 .findFirst().orElse(null);
452 }
453
454 private TrafficSelector buildSelector(Criterion... criteria) {
455
456
457 TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
458
459 for (Criterion c : criteria) {
460 sBuilder.add(c);
461 }
462
463 return sBuilder.build();
464 }
465
466 private TrafficTreatment buildTreatment(Instruction... instructions) {
467
468
469 TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
470
471 for (Instruction i : instructions) {
472 tBuilder.add(i);
473 }
474
475 return tBuilder.build();
476 }
477
478
479 private void fail(Objective obj, ObjectiveError error) {
480 if (obj.context().isPresent()) {
481 obj.context().get().onError(obj, error);
482 }
483 }
484
485 private void pass(Objective obj) {
486 if (obj.context().isPresent()) {
487 obj.context().get().onSuccess(obj);
488 }
489 }
490
alshabib2cc73cb2015-06-30 20:26:56 -0700491 /**
492 * Build a device description.
Jonathan Hart3e594642015-10-20 17:31:24 -0700493 *
alshabib2cc73cb2015-06-30 20:26:56 -0700494 * @param deviceId a deviceId
alshabibfd430b62015-12-16 18:56:38 -0800495 * @param key the key of the annotation
496 * @param value the value for the annotation
alshabib2cc73cb2015-06-30 20:26:56 -0700497 * @return a device description
498 */
499 private DeviceDescription description(DeviceId deviceId, String key, String value) {
500 DeviceService deviceService = serviceDirectory.get(DeviceService.class);
501 Device device = deviceService.getDevice(deviceId);
502
503 checkNotNull(device, "Device not found in device service.");
504
505 DefaultAnnotations.Builder builder = DefaultAnnotations.builder();
506 if (value != null) {
507 builder.set(key, value);
508 } else {
509 builder.remove(key);
510 }
511 return new DefaultDeviceDescription(device.id().uri(), device.type(),
512 device.manufacturer(), device.hwVersion(),
513 device.swVersion(), device.serialNumber(),
514 device.chassisId(), builder.build());
515 }
516
517 /**
518 * Simple ancillary provider used to annotate device.
519 */
520 private static final class AnnotationProvider
521 extends AbstractProvider implements DeviceProvider {
522 private AnnotationProvider() {
523 super(PID);
524 }
525
526 @Override
527 public void triggerProbe(DeviceId deviceId) {
528 }
529
530 @Override
531 public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
532 }
533
534 @Override
535 public boolean isReachable(DeviceId deviceId) {
536 return false;
537 }
538 }
539
alshabib0ccde6d2015-05-30 18:22:36 -0700540}