2 * Copyright (c) 2015 CableLabs and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.controller.packetcable.provider;
11 import static com.google.common.base.Preconditions.checkNotNull;
13 import com.google.common.base.Optional;
14 import com.google.common.collect.Maps;
15 import com.google.common.collect.Sets;
16 import com.google.common.util.concurrent.CheckedFuture;
17 import java.net.InetAddress;
18 import java.net.UnknownHostException;
19 import java.util.ArrayList;
20 import java.util.Collections;
21 import java.util.List;
24 import java.util.concurrent.ConcurrentHashMap;
25 import java.util.concurrent.ExecutionException;
26 import java.util.concurrent.Executor;
27 import java.util.concurrent.Executors;
28 import javax.annotation.Nonnull;
29 import javax.annotation.concurrent.ThreadSafe;
30 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
31 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
32 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
33 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
34 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
35 import org.opendaylight.controller.packetcable.provider.validation.DataValidator;
36 import org.opendaylight.controller.packetcable.provider.validation.ValidationException;
37 import org.opendaylight.controller.packetcable.provider.validation.Validator;
38 import org.opendaylight.controller.packetcable.provider.validation.impl.CcapsValidatorProviderFactory;
39 import org.opendaylight.controller.packetcable.provider.validation.impl.QosValidatorProviderFactory;
40 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
41 import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
42 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpPrefix;
43 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
44 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.Ccaps;
45 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.Qos;
46 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.ServiceClassName;
47 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.ServiceFlowDirection;
48 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.ccap.attributes.ConnectionBuilder;
49 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.ccaps.Ccap;
50 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.ccaps.CcapBuilder;
51 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.pcmm.qos.gates.Apps;
52 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.pcmm.qos.gates.apps.App;
53 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.pcmm.qos.gates.apps.AppBuilder;
54 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.pcmm.qos.gates.apps.AppKey;
55 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.pcmm.qos.gates.apps.app.Subscribers;
56 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.pcmm.qos.gates.apps.app.SubscribersBuilder;
57 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.pcmm.qos.gates.apps.app.subscribers.Subscriber;
58 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.pcmm.qos.gates.apps.app.subscribers.SubscriberBuilder;
59 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.pcmm.qos.gates.apps.app.subscribers.SubscriberKey;
60 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.pcmm.qos.gates.apps.app.subscribers.subscriber.Gates;
61 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.pcmm.qos.gates.apps.app.subscribers.subscriber.GatesBuilder;
62 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.pcmm.qos.gates.apps.app.subscribers.subscriber.gates.Gate;
63 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.pcmm.qos.gates.apps.app.subscribers.subscriber.gates.GateBuilder;
64 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.pcmm.qos.gates.apps.app.subscribers.subscriber.gates.GateKey;
65 import org.opendaylight.yangtools.concepts.ListenerRegistration;
66 import org.opendaylight.yangtools.yang.binding.DataObject;
67 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
68 import org.pcmm.rcd.IPCMMClient;
69 import org.slf4j.Logger;
70 import org.slf4j.LoggerFactory;
73 * Called by ODL framework to start this bundle.
75 * This class is responsible for processing messages received from ODL's restconf interface.
76 * TODO - Remove some of these state maps and move some of this into the PCMMService
79 public class PacketcableProvider implements BindingAwareProvider, AutoCloseable {
81 private static final Logger logger = LoggerFactory.getLogger(PacketcableProvider.class);
83 // keys to the /restconf/config/packetcable:ccaps and /restconf/config/packetcable:qos config datastore
84 private static final InstanceIdentifier<Ccaps> ccapsIID = InstanceIdentifier.builder(Ccaps.class).build();
85 private static final InstanceIdentifier<Qos> qosIID = InstanceIdentifier.builder(Qos.class).build();
87 // TODO - Revisit these maps and remove the ones no longer necessary
88 private final Map<String, Ccap> ccapMap = new ConcurrentHashMap<>();
89 private final Map<String, Gate> gateMap = new ConcurrentHashMap<>();
90 private final Map<String, String> gateCcapMap = new ConcurrentHashMap<>();
91 private final Map<Subnet, Ccap> subscriberSubnetsMap = new ConcurrentHashMap<>();
92 private final Map<ServiceClassName, List<Ccap>> downstreamScnMap = new ConcurrentHashMap<>();
93 private final Map<ServiceClassName, List<Ccap>> upstreamScnMap = new ConcurrentHashMap<>();
95 private final Executor executor = Executors.newSingleThreadExecutor();
98 * Holds a PCMMService object for each CCAP being managed.
100 private final Map<String, PCMMService> pcmmServiceMap = new ConcurrentHashMap<>();
103 * The ODL object used to broker messages throughout the framework
105 private DataBroker dataBroker;
106 private MdsalUtils mdsalUtils;
108 // Data change listeners/registrations
109 private final CcapsDataChangeListener ccapsDataChangeListener = new CcapsDataChangeListener();
110 private final QosDataChangeListener qosDataChangeListener = new QosDataChangeListener();
112 private ListenerRegistration<DataChangeListener> ccapsDataChangeListenerRegistration;
113 private ListenerRegistration<DataChangeListener> qosDataChangeListenerRegistration;
118 public PacketcableProvider() {
119 logger.info("Starting provider");
123 public void onSessionInitiated(ProviderContext session) {
124 logger.info("Packetcable Session Initiated");
125 logger.info("logging levels: error={}, warn={}, info={}, debug={}, trace={}", logger.isErrorEnabled(),
126 logger.isWarnEnabled(), logger.isInfoEnabled(), logger.isDebugEnabled(), logger.isTraceEnabled());
128 dataBroker = session.getSALService(DataBroker.class);
130 mdsalUtils = new MdsalUtils(dataBroker);
132 ccapsDataChangeListenerRegistration = dataBroker
133 .registerDataChangeListener(LogicalDatastoreType.CONFIGURATION, ccapsIID.child(Ccap.class),
134 ccapsDataChangeListener, DataBroker.DataChangeScope.SUBTREE);
136 qosDataChangeListenerRegistration = dataBroker
137 .registerDataChangeListener(LogicalDatastoreType.CONFIGURATION, PacketcableProvider.qosIID.child(Apps.class).child(App.class),
138 qosDataChangeListener, DataBroker.DataChangeScope.SUBTREE);
142 * Implemented from the AutoCloseable interface.
145 public void close() throws ExecutionException, InterruptedException {
146 if (ccapsDataChangeListenerRegistration != null) {
147 ccapsDataChangeListenerRegistration.close();
150 if (qosDataChangeListenerRegistration != null) {
151 qosDataChangeListenerRegistration.close();
155 private void updateCcapMaps(final Ccap ccap) {
156 // add ccap to the subscriberSubnets map
157 for (final IpPrefix ipPrefix : ccap.getSubscriberSubnets()) {
159 subscriberSubnetsMap.put(Subnet.createInstance(getIpPrefixStr(ipPrefix)), ccap);
160 } catch (UnknownHostException e) {
161 logger.error("updateSubscriberSubnets: {}:{} FAILED: {}", ipPrefix, ccap, e.getMessage());
164 // ccap to upstream SCN map
165 for (final ServiceClassName scn : ccap.getUpstreamScns()) {
166 if (upstreamScnMap.containsKey(scn)) {
167 upstreamScnMap.get(scn).add(ccap);
169 final List<Ccap> ccapList = new ArrayList<>();
171 upstreamScnMap.put(scn, ccapList);
174 // ccap to downstream SCN map
175 for (final ServiceClassName scn : ccap.getDownstreamScns()) {
176 if (downstreamScnMap.containsKey(scn)) {
177 downstreamScnMap.get(scn).add(ccap);
179 final List<Ccap> ccapList = new ArrayList<>();
181 downstreamScnMap.put(scn, ccapList);
186 private String getIpPrefixStr(final IpPrefix ipPrefix) {
187 final Ipv4Prefix ipv4 = ipPrefix.getIpv4Prefix();
189 return ipv4.getValue();
191 return ipPrefix.getIpv6Prefix().getValue();
195 public InetAddress getInetAddress(final String subId) {
197 return InetAddress.getByName(subId);
198 } catch (UnknownHostException e) {
199 logger.error("getInetAddress: {} FAILED: {}", subId, e.getMessage());
204 private Ccap findCcapForSubscriberId(final InetAddress inetAddr) {
205 // TODO replace this with a loading cache, https://github.com/google/guava/wiki/CachesExplained
206 Ccap matchedCcap = null;
207 int longestPrefixLen = -1;
208 for (final Map.Entry<Subnet, Ccap> entry : subscriberSubnetsMap.entrySet()) {
209 final Subnet subnet = entry.getKey();
210 if (subnet.isInNet(inetAddr)) {
211 int prefixLen = subnet.getPrefixLen();
212 if (prefixLen > longestPrefixLen) {
213 matchedCcap = entry.getValue();
214 longestPrefixLen = prefixLen;
221 private ServiceFlowDirection findScnOnCcap(final ServiceClassName scn, final Ccap ccap) {
225 if (upstreamScnMap.containsKey(scn)) {
226 final List<Ccap> ccapList = upstreamScnMap.get(scn);
227 if (ccapList.contains(ccap)) {
228 return ServiceFlowDirection.Us;
230 } else if (downstreamScnMap.containsKey(scn)) {
231 final List<Ccap> ccapList = downstreamScnMap.get(scn);
232 if (ccapList.contains(ccap)) {
233 return ServiceFlowDirection.Ds;
239 private void removeCcapFromAllMaps(final Ccap ccap) {
240 // remove the ccap from all maps
241 // subscriberSubnets map
242 for (final Map.Entry<Subnet, Ccap> entry : subscriberSubnetsMap.entrySet()) {
243 if (entry.getValue() == ccap) {
244 subscriberSubnetsMap.remove(entry.getKey());
247 // ccap to upstream SCN map
248 for (final Map.Entry<ServiceClassName, List<Ccap>> entry : upstreamScnMap.entrySet()) {
249 final List<Ccap> ccapList = entry.getValue();
250 ccapList.remove(ccap);
251 if (ccapList.isEmpty()) {
252 upstreamScnMap.remove(entry.getKey());
255 // ccap to downstream SCN map
256 for (final Map.Entry<ServiceClassName, List<Ccap>> entry : downstreamScnMap.entrySet()) {
257 final List<Ccap> ccapList = entry.getValue();
258 ccapList.remove(ccap);
259 if (ccapList.isEmpty()) {
260 downstreamScnMap.remove(entry.getKey());
264 final PCMMService service = pcmmServiceMap.remove(ccap.getCcapId());
265 if (service != null) {
270 // ValidationException does not need to be thrown again
271 @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
272 private <T extends DataObject> void saveErrors(@Nonnull Map<InstanceIdentifier<T>, ValidationException> errorMap,
273 @Nonnull Map<InstanceIdentifier<T>, T> dataMap) {
275 final WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
278 for (InstanceIdentifier<T> iid : errorMap.keySet()) {
280 final ValidationException exception = errorMap.get(iid);
281 final T badData = dataMap.get(iid);
283 if (!badData.getImplementedInterface().isAssignableFrom(iid.getTargetType())) {
284 // InstanceIdentifier<T> does not have the same type as the DataObject
285 logger.error("Bad InstanceIdentifier to DataObject mapping, {} : {}", iid, badData);
289 if (badData instanceof Ccap) {
290 final Ccap ccap = (Ccap) badData;
292 final Ccap opperationalCcap =
293 new CcapBuilder().setCcapId(ccap.getCcapId()).setError(exception.getErrorMessages()).build();
296 // type match between iid and badData is done at start of loop
297 @SuppressWarnings("unchecked")
298 final InstanceIdentifier<Ccap> ccapIID = (InstanceIdentifier<Ccap>) iid;
299 writeTransaction.put(LogicalDatastoreType.OPERATIONAL, ccapIID, opperationalCcap);
301 else if (badData instanceof Gate) {
302 final Gate gate = (Gate) badData;
304 final Gate operationalGate =
306 .setGateId(gate.getGateId())
307 .setError(exception.getErrorMessages())
310 final Gates operationalGates = new GatesBuilder()
311 .setGate(Collections.singletonList(operationalGate))
314 final SubscriberKey subscriberKey = InstanceIdentifier.keyOf(iid.firstIdentifierOf(Subscriber.class));
315 final Subscriber operationalSubscriber = new SubscriberBuilder()
316 .setSubscriberId(subscriberKey.getSubscriberId())
317 .setGates(operationalGates)
320 final Subscribers operationalSubscribers = new SubscribersBuilder()
321 .setSubscriber(Collections.singletonList(operationalSubscriber))
324 final InstanceIdentifier<App> appIID = iid.firstIdentifierOf(App.class);
325 final AppKey appKey = InstanceIdentifier.keyOf(appIID);
326 final App operationalApp = new AppBuilder()
327 .setAppId(appKey.getAppId())
328 .setSubscribers(operationalSubscribers)
332 writeTransaction.put(LogicalDatastoreType.OPERATIONAL, appIID, operationalApp);
335 // If you get here a developer forgot to add a type above
336 logger.error("Unexpected type requested for error saving: {}", badData);
337 throw new IllegalStateException("Unsupported type for error saving");
343 CheckedFuture<Void, TransactionCommitFailedException> future = writeTransaction.submit();
347 } catch (TransactionCommitFailedException e) {
348 logger.error("Failed to write errors to operational datastore", e);
353 * Removes Ccaps if all Ccap instances are removed
355 private class CcapsCleaner extends AbstractCleaner<Ccaps> {
357 public CcapsCleaner(final InstanceIdentifier<?> removedIID) {
358 super(removedIID, Ccaps.class, LogicalDatastoreType.OPERATIONAL);
362 protected boolean shouldClean(final Ccaps ccaps) {
363 return ccaps.getCcap().isEmpty();
368 * Removes Subscriber if all Gate instances are removed
370 private class SubscriberCleaner extends AbstractCleaner<Subscriber> {
372 public SubscriberCleaner(InstanceIdentifier<Gate> removedGateIID) {
373 super(removedGateIID, Subscriber.class, LogicalDatastoreType.OPERATIONAL);
377 protected boolean shouldClean(final Subscriber subscriber) {
378 return subscriber.getGates().getGate().isEmpty();
382 protected void postRemove(InstanceIdentifier<Subscriber> subscriberIID) {
383 executor.execute(new AppCleaner(subscriberIID));
389 * Removes App if all Subscribers are removed.
391 private class AppCleaner extends AbstractCleaner<App> {
393 public AppCleaner(InstanceIdentifier<Subscriber> removedSubscriberIID) {
394 super(removedSubscriberIID, App.class, LogicalDatastoreType.OPERATIONAL);
398 boolean shouldClean(final App app) {
399 return app.getSubscribers().getSubscriber().isEmpty();
403 void postRemove(final InstanceIdentifier<App> appIID) {
404 executor.execute(new AppsCleaner(appIID));
410 * Removes Apps if all App instances are removed.
412 private class AppsCleaner extends AbstractCleaner<Apps> {
414 public AppsCleaner(InstanceIdentifier<App> removedAppIID) {
415 super(removedAppIID, Apps.class, LogicalDatastoreType.OPERATIONAL);
419 protected boolean shouldClean(final Apps apps) {
420 return apps.getApp().isEmpty();
426 * Helper class to do the heavy lifting in removing object. Lets subclasses decide with
427 * {@link #shouldClean(DataObject)}. <br>
429 * Subclasses can react after an instance is removed by overriding {@link #postRemove(InstanceIdentifier)}
430 * @param <T> The type that will be removed
432 private abstract class AbstractCleaner <T extends DataObject> implements Runnable {
433 final InstanceIdentifier<?> removedIID;
434 final Class<T> tClass;
435 final LogicalDatastoreType datastoreType;
437 public AbstractCleaner(InstanceIdentifier<?> removedIID, Class<T> tClass, LogicalDatastoreType datastoreType) {
438 this.removedIID = checkNotNull(removedIID);
439 this.tClass = checkNotNull(tClass);
440 this.datastoreType = checkNotNull(datastoreType);
445 InstanceIdentifier<T> tIID = removedIID.firstIdentifierOf(tClass);
447 Optional<T> optional = mdsalUtils.read(datastoreType, tIID);
448 if (optional.isPresent()) {
450 if (shouldClean(optional.get())) {
451 if (mdsalUtils.delete(datastoreType, tIID)) {
462 logger.error("Expected to find InstanceIdentifier<{}> but was not found: {}",
463 tClass.getSimpleName(), removedIID);
468 * If returns true the object will be removed from the datastore
469 * @param object The object that might be removed.
470 * @return true if it should be removed.
472 abstract boolean shouldClean(final T object);
475 * Called after an instance is removed.
476 * @param tIID the InstanceIdentifier of the removed object
478 void postRemove(InstanceIdentifier<T> tIID) {
482 void removeFailed(InstanceIdentifier<T> tIID) {
483 logger.error("Failed to remove {}", tIID);
489 * Listener for the packetcable:ccaps tree
491 private class CcapsDataChangeListener extends AbstractDataChangeListener<Ccap> {
493 private final DataValidator ccapsDataValidator = new DataValidator(new CcapsValidatorProviderFactory().build());
495 private final Set<InstanceIdentifier<Ccap>> updateQueue = Sets.newConcurrentHashSet();
497 public CcapsDataChangeListener() {
502 protected void handleCreatedData(final Map<InstanceIdentifier<Ccap>, Ccap> createdCcaps) {
503 if (createdCcaps.isEmpty()) {
507 final Map<InstanceIdentifier<Ccap>, ValidationException> errorMap =
508 ccapsDataValidator.validateOneType(createdCcaps, Validator.Extent.NODE_AND_SUBTREE);
510 // validate all new objects an update operational datastore
511 if (!errorMap.isEmpty()) {
512 // bad data write errors to operational datastore
513 saveErrors(errorMap, createdCcaps);
516 if (createdCcaps.size() > errorMap.size()) {
517 final Map<InstanceIdentifier<Ccap>, Ccap> goodData =
518 Maps.newHashMapWithExpectedSize(createdCcaps.size() - errorMap.size());
519 for (InstanceIdentifier<Ccap> iid : createdCcaps.keySet()) {
520 if (!errorMap.containsKey(iid)) {
521 goodData.put(iid, createdCcaps.get(iid));
524 addNewCcaps(goodData);
528 private void addNewCcaps(final Map<InstanceIdentifier<Ccap>, Ccap> goodData) {
529 for (InstanceIdentifier<Ccap> iid : goodData.keySet()) {
530 final Ccap ccap = goodData.get(iid);
533 if (pcmmServiceMap.containsKey(ccap.getCcapId())) {
534 logger.error("Already monitoring CCAP - " + ccap);
537 final PCMMService pcmmService = new PCMMService(IPCMMClient.CLIENT_TYPE, ccap);
538 // TODO - may want to use the AMID but for the client type but probably not???
540 final PCMMService pcmmService = new PCMMService(
541 thisCcap.getAmId().getAmType().shortValue(), thisCcap);
543 ConnectionBuilder connectionBuilder = new ConnectionBuilder();
544 String message = pcmmService.addCcap();
545 if (message.contains("200 OK")) {
546 pcmmServiceMap.put(ccap.getCcapId(), pcmmService);
547 ccapMap.put(ccap.getCcapId(), ccap);
548 updateCcapMaps(ccap);
549 logger.info("Created CCAP: {}/{} : {}", iid, ccap, message);
550 logger.info("Created CCAP: {} : {}", iid, message);
551 connectionBuilder.setConnected(true).setError(Collections.<String>emptyList());
553 logger.error("Create CCAP Failed: {} : {}", iid, message);
555 connectionBuilder.setConnected(false).setError(Collections.singletonList(message));
558 Optional<Ccap> optionalCcap = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, iid);
560 final CcapBuilder responseCcapBuilder;
561 if (optionalCcap.isPresent()) {
562 responseCcapBuilder = new CcapBuilder(optionalCcap.get());
564 responseCcapBuilder = new CcapBuilder();
565 responseCcapBuilder.setCcapId(ccap.getCcapId());
568 responseCcapBuilder.setConnection(connectionBuilder.build());
570 mdsalUtils.put(LogicalDatastoreType.OPERATIONAL, iid, responseCcapBuilder.build());
576 protected void handleUpdatedData(final Map<InstanceIdentifier<Ccap>, Ccap> updatedCcaps,
577 final Map<InstanceIdentifier<Ccap>, Ccap> originalCcaps) {
579 // TODO actually support updates
581 // update operation not allowed -- restore the original config object and complain
582 for (final Map.Entry<InstanceIdentifier<Ccap>, Ccap> entry : updatedCcaps.entrySet()) {
583 if (!originalCcaps.containsKey(entry.getKey())) {
584 logger.error("No original data found for supposedly updated data: {}", entry.getValue());
588 // If this notification is coming from our modification ignore it.
589 if (updateQueue.contains(entry.getKey())) {
590 updateQueue.remove(entry.getKey());
594 final Ccap originalCcap = originalCcaps.get(entry.getKey());
595 //final Ccap updatedCcap = entry.getValue();
597 // restore the original data
598 updateQueue.add(entry.getKey());
599 mdsalUtils.put(LogicalDatastoreType.CONFIGURATION, entry.getKey(), originalCcap);
600 logger.error("CCAP update not permitted {}", entry.getKey());
605 protected void handleRemovedData(final Set<InstanceIdentifier<Ccap>> removedCcapPaths,
606 final Map<InstanceIdentifier<Ccap>, Ccap> originalCcaps) {
608 for (InstanceIdentifier<Ccap> iid : removedCcapPaths) {
609 final Ccap nukedCcap = originalCcaps.get(iid);
610 removeCcapFromAllMaps(nukedCcap);
612 mdsalUtils.delete(LogicalDatastoreType.OPERATIONAL, iid);
614 // clean up ccaps level if it is now empty
615 executor.execute(new CcapsCleaner(iid));
622 private class QosDataChangeListener extends AbstractDataChangeListener<Gate> {
624 private final DataValidator qosDataValidator = new DataValidator(new QosValidatorProviderFactory().build());
625 private final Set<InstanceIdentifier<Gate>> updateQueue = Sets.newConcurrentHashSet();
627 public QosDataChangeListener() {
632 protected void handleCreatedData(final Map<InstanceIdentifier<Gate>, Gate> createdData) {
634 final Map<InstanceIdentifier<Gate>, ValidationException> errorMap =
635 qosDataValidator.validateOneType(createdData, Validator.Extent.NODE_AND_SUBTREE);
637 // validate all new objects an update operational datastore
638 if (!errorMap.isEmpty()) {
639 // bad data write errors to operational datastore
640 saveErrors(errorMap, createdData);
643 if (createdData.size() > errorMap.size()) {
644 final Map<InstanceIdentifier<Gate>, Gate> goodData =
645 Maps.newHashMapWithExpectedSize(createdData.size() - errorMap.size());
646 for (InstanceIdentifier<Gate> iid : createdData.keySet()) {
647 if (!errorMap.containsKey(iid)) {
648 goodData.put(iid, createdData.get(iid));
651 addNewGates(goodData);
656 private void addNewGates(final Map<InstanceIdentifier<Gate>, Gate> createdGates) {
658 for (InstanceIdentifier<Gate> gateIID : createdGates.keySet()) {
659 final Gate newGate = createdGates.get(gateIID);
661 final String newGatePathStr = makeGatePathString(gateIID);
663 final InstanceIdentifier<Subscriber> subscriberIID = gateIID.firstIdentifierOf(Subscriber.class);
664 final SubscriberKey subscriberKey = InstanceIdentifier.keyOf(subscriberIID);
665 final InetAddress subscriberAddr = getInetAddress(subscriberKey.getSubscriberId());
666 if (subscriberAddr == null) {
667 final String msg = String.format("subscriberId must be a valid ipaddress: %s",
668 subscriberKey.getSubscriberId());
670 saveGateError(gateIID, newGatePathStr, msg);
674 final Ccap ccap = findCcapForSubscriberId(subscriberAddr);
676 final String msg = String.format("Unable to find Ccap for subscriber %s: @ %s",
677 subscriberKey.getSubscriberId(), newGatePathStr);
679 saveGateError(gateIID, newGatePathStr, msg);
683 final ServiceClassName scn = newGate.getTrafficProfile().getServiceClassName();
684 final ServiceFlowDirection scnDirection = findScnOnCcap(scn, ccap);
685 if (scnDirection == null) {
686 final String msg = String.format("SCN %s not found on CCAP %s for %s",
687 scn, ccap.getCcapId(), newGatePathStr);
689 saveGateError(gateIID, newGatePathStr, msg);
693 final PCMMService pcmmService = pcmmServiceMap.get(ccap.getCcapId());
694 if (pcmmService == null) {
695 final String msg = String.format("Unable to locate PCMM Service for CCAP: %s ; with subscriber: %s",
696 ccap, subscriberKey.getSubscriberId());
698 saveGateError(gateIID, newGatePathStr, msg);
702 PCMMService.GateSetStatus status = pcmmService.sendGateSet(newGatePathStr, subscriberAddr, newGate, scnDirection);
703 if (status.didSucceed()) {
704 gateMap.put(newGatePathStr, newGate);
705 gateCcapMap.put(newGatePathStr, ccap.getCcapId());
707 final GateBuilder gateBuilder = new GateBuilder();
708 gateBuilder.setGateId(newGate.getGateId())
709 .setGatePath(newGatePathStr)
710 .setCcapId(ccap.getCcapId())
711 .setCopsGateId(status.getCopsGateId())
712 .setCopsState(status.didSucceed() ? "success" : "failure");
713 if (!status.didSucceed()) {
714 gateBuilder.setError(Collections.singletonList(status.getMessage()));
717 Gate operationalGate = gateBuilder.build();
719 mdsalUtils.put(LogicalDatastoreType.OPERATIONAL, gateIID, operationalGate);
725 private void saveGateError(@Nonnull final InstanceIdentifier<Gate> gateIID, @Nonnull final String gatePathStr,
726 @Nonnull final String error) {
727 checkNotNull(gateIID);
730 final GateBuilder gateBuilder = new GateBuilder();
731 gateBuilder.setGateId(InstanceIdentifier.keyOf(gateIID).getGateId())
732 .setGatePath(gatePathStr)
734 .setCopsState("N/A");
736 gateBuilder.setError(Collections.singletonList(error));
738 Gate operationalGate = gateBuilder.build();
740 mdsalUtils.put(LogicalDatastoreType.OPERATIONAL, gateIID, operationalGate);
744 protected void handleUpdatedData(final Map<InstanceIdentifier<Gate>, Gate> updatedData,
745 final Map<InstanceIdentifier<Gate>, Gate> originalData) {
746 // TODO actually support updates
748 // update operation not allowed -- restore the original config object and complain
749 for (final Map.Entry<InstanceIdentifier<Gate>, Gate> entry : updatedData.entrySet()) {
750 if (!originalData.containsKey(entry.getKey())) {
751 logger.error("No original data found for supposedly updated data: {}", entry.getValue());
755 // If this notification is coming from our modification ignore it.
756 if (updateQueue.contains(entry.getKey())) {
757 updateQueue.remove(entry.getKey());
761 final Gate originalGate = originalData.get(entry.getKey());
763 // restores the original data
764 updateQueue.add(entry.getKey());
765 mdsalUtils.put(LogicalDatastoreType.CONFIGURATION, entry.getKey(), originalGate);
766 logger.error("Update not permitted {}", entry.getKey());
774 protected void handleRemovedData(final Set<InstanceIdentifier<Gate>> removedPaths,
775 final Map<InstanceIdentifier<Gate>, Gate> originalData) {
777 for (final InstanceIdentifier<Gate> removedGateIID : removedPaths) {
779 mdsalUtils.delete(LogicalDatastoreType.OPERATIONAL, removedGateIID);
781 executor.execute(new SubscriberCleaner(removedGateIID));
783 final String gatePathStr = makeGatePathString(removedGateIID);
785 if (gateMap.containsKey(gatePathStr)) {
786 final Gate thisGate = gateMap.remove(gatePathStr);
787 final String gateId = thisGate.getGateId();
788 final String ccapId = gateCcapMap.remove(gatePathStr);
789 final Ccap thisCcap = ccapMap.get(ccapId);
790 final PCMMService service = pcmmServiceMap.get(thisCcap.getCcapId());
791 if (service != null) {
792 service.sendGateDelete(gatePathStr);
793 logger.info("onDataChanged(): removed QoS gate {} for {}/{}/{}: ", gateId, ccapId, gatePathStr,
797 "Unable to send to locate PCMMService to send gate delete message with CCAP - " + thisCcap);
806 private String makeGatePathString(InstanceIdentifier<Gate> iid) {
807 final InstanceIdentifier<App> appIID = iid.firstIdentifierOf(App.class);
808 final AppKey appKey = InstanceIdentifier.keyOf(appIID);
810 final InstanceIdentifier<Subscriber> subscriberIID = iid.firstIdentifierOf(Subscriber.class);
811 final SubscriberKey subscriberKey = InstanceIdentifier.keyOf(subscriberIID);
813 final GateKey gateKey = InstanceIdentifier.keyOf(iid);
815 return appKey.getAppId()
816 + "/" + subscriberKey.getSubscriberId()
817 + "/" + gateKey.getGateId();