Updates to support new TrafficProfiles that require a user-specified Direction in...
[packetcable.git] / packetcable-policy-server / src / main / java / org / opendaylight / controller / packetcable / provider / PacketcableProvider.java
1 /*
2  * Copyright (c) 2015 CableLabs and others.  All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.controller.packetcable.provider;
10
11 import static com.google.common.base.Preconditions.checkNotNull;
12
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 com.google.common.util.concurrent.Futures;
18
19 import java.net.InetAddress;
20 import java.net.UnknownHostException;
21 import java.text.DateFormat;
22 import java.text.SimpleDateFormat;
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.Date;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Objects;
29 import java.util.Set;
30 import java.util.concurrent.ConcurrentHashMap;
31 import java.util.concurrent.ExecutionException;
32 import java.util.concurrent.Executor;
33 import java.util.concurrent.Executors;
34 import java.util.concurrent.Future;
35
36 import javax.annotation.Nonnull;
37 import javax.annotation.concurrent.ThreadSafe;
38
39 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
40 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
41 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
42 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
43 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
44 import org.opendaylight.controller.packetcable.provider.validation.DataValidator;
45 import org.opendaylight.controller.packetcable.provider.validation.ValidationException;
46 import org.opendaylight.controller.packetcable.provider.validation.Validator;
47 import org.opendaylight.controller.packetcable.provider.validation.impl.CcapsValidatorProviderFactory;
48 import org.opendaylight.controller.packetcable.provider.validation.impl.QosValidatorProviderFactory;
49 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
50 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration;
51 import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
52 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
53 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
54 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.DateAndTime;
55 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.AppContext;
56 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.CcapContext;
57 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.CcapPollConnectionInput;
58 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.CcapPollConnectionOutput;
59 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.CcapPollConnectionOutputBuilder;
60 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.CcapSetConnectionInput;
61 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.CcapSetConnectionOutput;
62 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.CcapSetConnectionOutputBuilder;
63 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.Ccaps;
64 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.PacketcableService;
65 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.Qos;
66 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.QosPollGatesInput;
67 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.QosPollGatesOutput;
68 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.QosPollGatesOutputBuilder;
69 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.ServiceClassName;
70 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.ServiceFlowDirection;
71 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.ccap.attributes.ConnectionBuilder;
72 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.ccaps.Ccap;
73 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.ccaps.CcapBuilder;
74 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.pcmm.qos.gate.spec.GateSpec;
75 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.pcmm.qos.gate.spec.GateSpecBuilder;
76 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.pcmm.qos.gates.Apps;
77 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.pcmm.qos.gates.apps.App;
78 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.pcmm.qos.gates.apps.AppBuilder;
79 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.pcmm.qos.gates.apps.AppKey;
80 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.pcmm.qos.gates.apps.app.Subscribers;
81 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.pcmm.qos.gates.apps.app.SubscribersBuilder;
82 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.pcmm.qos.gates.apps.app.subscribers.Subscriber;
83 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.pcmm.qos.gates.apps.app.subscribers.SubscriberBuilder;
84 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.pcmm.qos.gates.apps.app.subscribers.SubscriberKey;
85 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.pcmm.qos.gates.apps.app.subscribers.subscriber.Gates;
86 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.pcmm.qos.gates.apps.app.subscribers.subscriber.GatesBuilder;
87 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.pcmm.qos.gates.apps.app.subscribers.subscriber.gates.Gate;
88 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.pcmm.qos.gates.apps.app.subscribers.subscriber.gates.GateBuilder;
89 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.pcmm.qos.gates.apps.app.subscribers.subscriber.gates.GateKey;
90 import org.opendaylight.yangtools.concepts.ListenerRegistration;
91 import org.opendaylight.yangtools.yang.binding.DataObject;
92 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
93 import org.opendaylight.yangtools.yang.common.RpcResult;
94 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
95 import org.pcmm.gates.impl.DOCSISServiceClassNameTrafficProfile;
96 import org.pcmm.gates.IGateSpec.Direction;
97 import org.pcmm.rcd.IPCMMClient;
98 import org.slf4j.Logger;
99 import org.slf4j.LoggerFactory;
100
101 /**
102  * Called by ODL framework to start this bundle.
103  * <p>
104  * This class is responsible for processing messages received from ODL's restconf interface.
105  * TODO - Remove some of these state maps and move some of this into the PCMMService
106  * TODO Don't implement PacketcableService, move that into an inner class
107  */
108 @ThreadSafe
109 public class PacketcableProvider implements BindingAwareProvider, AutoCloseable, PacketcableService {
110
111     private static final Logger logger = LoggerFactory.getLogger(PacketcableProvider.class);
112
113     // keys to the /restconf/config/packetcable:ccaps and /restconf/config/packetcable:qos config datastore
114     private static final InstanceIdentifier<Ccaps> ccapsIID = InstanceIdentifier.builder(Ccaps.class).build();
115     private static final InstanceIdentifier<Qos> qosIID = InstanceIdentifier.builder(Qos.class).build();
116
117     // TODO - Revisit these maps and remove the ones no longer necessary
118     private final Map<String, Ccap> ccapMap = new ConcurrentHashMap<>();
119     private final Map<String, Gate> gateMap = new ConcurrentHashMap<>();
120     private final Map<String, String> gateCcapMap = new ConcurrentHashMap<>();
121     private final Map<Subnet, Ccap> subscriberSubnetsMap = new ConcurrentHashMap<>();
122     private final Map<ServiceClassName, List<Ccap>> downstreamScnMap = new ConcurrentHashMap<>();
123     private final Map<ServiceClassName, List<Ccap>> upstreamScnMap = new ConcurrentHashMap<>();
124
125     private final Executor executor = Executors.newSingleThreadExecutor();
126
127     /**
128      * Holds a PCMMService object for each CCAP being managed.
129      */
130     private final Map<String, PCMMService> pcmmServiceMap = new ConcurrentHashMap<>();
131
132     /**
133      * The ODL object used to broker messages throughout the framework
134      */
135     private DataBroker dataBroker;
136     private MdsalUtils mdsalUtils;
137
138     //Routed RPC Registration
139     private RoutedRpcRegistration<PacketcableService> rpcRegistration;
140
141     // Data change listeners/registrations
142     private final CcapsDataChangeListener ccapsDataChangeListener = new CcapsDataChangeListener();
143     private final QosDataChangeListener qosDataChangeListener = new QosDataChangeListener();
144
145     private ListenerRegistration<DataChangeListener> ccapsDataChangeListenerRegistration;
146     private ListenerRegistration<DataChangeListener> qosDataChangeListenerRegistration;
147
148     /**
149      * Constructor
150      */
151     public PacketcableProvider() {
152         logger.info("Starting provider");
153     }
154
155     @Override
156     public void onSessionInitiated(ProviderContext session) {
157         logger.info("Packetcable Session Initiated");
158         logger.info("logging levels: error={}, warn={}, info={}, debug={}, trace={}", logger.isErrorEnabled(), logger.isWarnEnabled(), logger.isInfoEnabled(), logger.isDebugEnabled(), logger.isTraceEnabled());
159
160         dataBroker = session.getSALService(DataBroker.class);
161
162         mdsalUtils = new MdsalUtils(dataBroker);
163
164         ccapsDataChangeListenerRegistration =
165                 dataBroker.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION, ccapsIID.child(Ccap.class),
166                         ccapsDataChangeListener, DataBroker.DataChangeScope.SUBTREE);
167
168         qosDataChangeListenerRegistration = dataBroker.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
169                 PacketcableProvider.qosIID.child(Apps.class).child(App.class), qosDataChangeListener,
170                 DataBroker.DataChangeScope.SUBTREE);
171
172         rpcRegistration = session.addRoutedRpcImplementation(PacketcableService.class, this);
173         logger.info("onSessionInitiated().rpcRgistration: {}", rpcRegistration);
174
175     }
176
177     /**
178      * Implemented from the AutoCloseable interface.
179      */
180     @Override
181     public void close() throws ExecutionException, InterruptedException {
182         if (ccapsDataChangeListenerRegistration != null) {
183             ccapsDataChangeListenerRegistration.close();
184         }
185
186         if (qosDataChangeListenerRegistration != null) {
187             qosDataChangeListenerRegistration.close();
188         }
189     }
190
191     private void updateCcapMaps(final Ccap ccap) {
192         // add ccap to the subscriberSubnets map
193         for (final IpPrefix ipPrefix : ccap.getSubscriberSubnets()) {
194             try {
195                 subscriberSubnetsMap.put(Subnet.createInstance(getIpPrefixStr(ipPrefix)), ccap);
196             } catch (UnknownHostException e) {
197                 logger.error("updateSubscriberSubnets: {}:{} FAILED: {}", ipPrefix, ccap, e.getMessage());
198             }
199         }
200         // ccap to upstream SCN map
201         for (final ServiceClassName scn : ccap.getUpstreamScns()) {
202             if (upstreamScnMap.containsKey(scn)) {
203                 upstreamScnMap.get(scn).add(ccap);
204             } else {
205                 final List<Ccap> ccapList = new ArrayList<>();
206                 ccapList.add(ccap);
207                 upstreamScnMap.put(scn, ccapList);
208             }
209         }
210         // ccap to downstream SCN map
211         for (final ServiceClassName scn : ccap.getDownstreamScns()) {
212             if (downstreamScnMap.containsKey(scn)) {
213                 downstreamScnMap.get(scn).add(ccap);
214             } else {
215                 final List<Ccap> ccapList = new ArrayList<>();
216                 ccapList.add(ccap);
217                 downstreamScnMap.put(scn, ccapList);
218             }
219         }
220     }
221
222     private String getIpPrefixStr(final IpPrefix ipPrefix) {
223         final Ipv4Prefix ipv4 = ipPrefix.getIpv4Prefix();
224         if (ipv4 != null) {
225             return ipv4.getValue();
226         } else {
227             return ipPrefix.getIpv6Prefix().getValue();
228         }
229     }
230
231     public InetAddress getInetAddress(final String subId) {
232         try {
233             return InetAddress.getByName(subId);
234         } catch (UnknownHostException e) {
235             logger.error("getInetAddress: {} FAILED: {}", subId, e.getMessage());
236             return null;
237         }
238     }
239
240     private Ccap findCcapForSubscriberId(final InetAddress inetAddr) {
241         // TODO replace this with a loading cache, https://github.com/google/guava/wiki/CachesExplained
242         Ccap matchedCcap = null;
243         int longestPrefixLen = -1;
244         for (final Map.Entry<Subnet, Ccap> entry : subscriberSubnetsMap.entrySet()) {
245             final Subnet subnet = entry.getKey();
246             if (subnet.isInNet(inetAddr)) {
247                 int prefixLen = subnet.getPrefixLen();
248                 if (prefixLen > longestPrefixLen) {
249                     matchedCcap = entry.getValue();
250                     longestPrefixLen = prefixLen;
251                 }
252             }
253         }
254         return matchedCcap;
255     }
256
257     private ServiceFlowDirection findScnOnCcap(final ServiceClassName scn, final Ccap ccap) {
258         checkNotNull(scn);
259         checkNotNull(ccap);
260
261         if (upstreamScnMap.containsKey(scn)) {
262             final List<Ccap> ccapList = upstreamScnMap.get(scn);
263             if (ccapList.contains(ccap)) {
264                 return ServiceFlowDirection.Us;
265             }
266         } else if (downstreamScnMap.containsKey(scn)) {
267             final List<Ccap> ccapList = downstreamScnMap.get(scn);
268             if (ccapList.contains(ccap)) {
269                 return ServiceFlowDirection.Ds;
270             }
271         }
272         return null;
273     }
274
275     private void removeCcapFromAllMaps(final Ccap ccap) {
276         // remove the ccap from all maps
277         // subscriberSubnets map
278         for (final Map.Entry<Subnet, Ccap> entry : subscriberSubnetsMap.entrySet()) {
279             if (entry.getValue() == ccap) {
280                 subscriberSubnetsMap.remove(entry.getKey());
281             }
282         }
283         // ccap to upstream SCN map
284         for (final Map.Entry<ServiceClassName, List<Ccap>> entry : upstreamScnMap.entrySet()) {
285             final List<Ccap> ccapList = entry.getValue();
286             ccapList.remove(ccap);
287             if (ccapList.isEmpty()) {
288                 upstreamScnMap.remove(entry.getKey());
289             }
290         }
291         // ccap to downstream SCN map
292         for (final Map.Entry<ServiceClassName, List<Ccap>> entry : downstreamScnMap.entrySet()) {
293             final List<Ccap> ccapList = entry.getValue();
294             ccapList.remove(ccap);
295             if (ccapList.isEmpty()) {
296                 downstreamScnMap.remove(entry.getKey());
297             }
298         }
299
300         final PCMMService service = pcmmServiceMap.remove(ccap.getCcapId());
301         if (service != null) {
302             service.disconect();
303         }
304     }
305
306     // ValidationException does not need to be thrown again
307     @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
308     private <T extends DataObject> void saveErrors(@Nonnull Map<InstanceIdentifier<T>, ValidationException> errorMap,
309                                                    @Nonnull Map<InstanceIdentifier<T>, T> dataMap) {
310
311         final WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
312
313
314         for (InstanceIdentifier<T> iid : errorMap.keySet()) {
315
316             final ValidationException exception = errorMap.get(iid);
317             final T badData = dataMap.get(iid);
318
319             if (!badData.getImplementedInterface().isAssignableFrom(iid.getTargetType())) {
320                 // InstanceIdentifier<T> does not have the same type as the DataObject
321                 logger.error("Bad InstanceIdentifier to DataObject mapping, {} : {}", iid, badData);
322                 continue;
323             }
324
325             if (badData instanceof Ccap) {
326                 final Ccap ccap = (Ccap) badData;
327
328                 final Ccap opperationalCcap =
329                         new CcapBuilder().setCcapId(ccap.getCcapId()).setError(exception.getErrorMessages()).build();
330
331
332                 // type match between iid and badData is done at start of loop
333                 @SuppressWarnings("unchecked") final InstanceIdentifier<Ccap> ccapIID = (InstanceIdentifier<Ccap>) iid;
334                 writeTransaction.put(LogicalDatastoreType.OPERATIONAL, ccapIID, opperationalCcap);
335             } else if (badData instanceof Gate) {
336                 final Gate gate = (Gate) badData;
337
338                 final Gate operationalGate =
339                         new GateBuilder().setGateId(gate.getGateId()).setError(exception.getErrorMessages()).build();
340
341                 final Gates operationalGates =
342                         new GatesBuilder().setGate(Collections.singletonList(operationalGate)).build();
343
344                 final SubscriberKey subscriberKey = InstanceIdentifier.keyOf(iid.firstIdentifierOf(Subscriber.class));
345                 final Subscriber operationalSubscriber =
346                         new SubscriberBuilder().setSubscriberId(subscriberKey.getSubscriberId())
347                                 .setGates(operationalGates)
348                                 .build();
349
350                 final Subscribers operationalSubscribers =
351                         new SubscribersBuilder().setSubscriber(Collections.singletonList(operationalSubscriber))
352                                 .build();
353
354                 final InstanceIdentifier<App> appIID = iid.firstIdentifierOf(App.class);
355                 final AppKey appKey = InstanceIdentifier.keyOf(appIID);
356                 final App operationalApp =
357                         new AppBuilder().setAppId(appKey.getAppId()).setSubscribers(operationalSubscribers).build();
358
359
360                 writeTransaction.put(LogicalDatastoreType.OPERATIONAL, appIID, operationalApp);
361             } else {
362                 // If you get here a developer forgot to add a type above
363                 logger.error("Unexpected type requested for error saving: {}", badData);
364                 throw new IllegalStateException("Unsupported type for error saving");
365             }
366
367         }
368
369
370         CheckedFuture<Void, TransactionCommitFailedException> future = writeTransaction.submit();
371
372         try {
373             future.checkedGet();
374         } catch (TransactionCommitFailedException e) {
375             logger.error("Failed to write errors to operational datastore", e);
376         }
377     }
378
379     /**
380      * Removes Ccaps if all Ccap instances are removed
381      */
382     private class CcapsCleaner extends AbstractCleaner<Ccaps> {
383
384         public CcapsCleaner(final InstanceIdentifier<?> removedIID) {
385             super(removedIID, Ccaps.class, LogicalDatastoreType.OPERATIONAL);
386         }
387
388         @Override
389         protected boolean shouldClean(final Ccaps ccaps) {
390             return ccaps.getCcap().isEmpty();
391         }
392     }
393
394
395     /**
396      * Removes Subscriber if all Gate instances are removed
397      */
398     private class SubscriberCleaner extends AbstractCleaner<Subscriber> {
399
400         public SubscriberCleaner(InstanceIdentifier<Gate> removedGateIID) {
401             super(removedGateIID, Subscriber.class, LogicalDatastoreType.OPERATIONAL);
402         }
403
404         @Override
405         protected boolean shouldClean(final Subscriber subscriber) {
406             return subscriber.getGates().getGate().isEmpty();
407         }
408
409         @Override
410         protected void postRemove(InstanceIdentifier<Subscriber> subscriberIID) {
411             executor.execute(new AppCleaner(subscriberIID));
412         }
413     }
414
415
416     /**
417      * Removes App if all Subscribers are removed.
418      */
419     private class AppCleaner extends AbstractCleaner<App> {
420
421         public AppCleaner(InstanceIdentifier<Subscriber> removedSubscriberIID) {
422             super(removedSubscriberIID, App.class, LogicalDatastoreType.OPERATIONAL);
423         }
424
425         @Override
426         boolean shouldClean(final App app) {
427             return app.getSubscribers().getSubscriber().isEmpty();
428         }
429
430         @Override
431         void postRemove(final InstanceIdentifier<App> appIID) {
432             //unregister app rpc path
433             logger.info("Un-Registering App Routed RPC Path...");
434             rpcRegistration.unregisterPath(AppContext.class, appIID);
435             executor.execute(new AppsCleaner(appIID));
436         }
437     }
438
439
440     /**
441      * Removes Apps if all App instances are removed.
442      */
443     private class AppsCleaner extends AbstractCleaner<Apps> {
444
445         public AppsCleaner(InstanceIdentifier<App> removedAppIID) {
446             super(removedAppIID, Apps.class, LogicalDatastoreType.OPERATIONAL);
447         }
448
449         @Override
450         protected boolean shouldClean(final Apps apps) {
451             return apps.getApp().isEmpty();
452         }
453     }
454
455
456     /**
457      * Helper class to do the heavy lifting in removing object. Lets subclasses decide with
458      * {@link #shouldClean(DataObject)}. <br>
459      * <p>
460      * Subclasses can react after an instance is removed by overriding {@link #postRemove(InstanceIdentifier)}
461      *
462      * @param <T>
463      *         The type that will be removed
464      */
465     private abstract class AbstractCleaner<T extends DataObject> implements Runnable {
466         final InstanceIdentifier<?> removedIID;
467         final Class<T> tClass;
468         final LogicalDatastoreType datastoreType;
469
470         public AbstractCleaner(InstanceIdentifier<?> removedIID, Class<T> tClass, LogicalDatastoreType datastoreType) {
471             this.removedIID = checkNotNull(removedIID);
472             this.tClass = checkNotNull(tClass);
473             this.datastoreType = checkNotNull(datastoreType);
474         }
475
476         @Override
477         public void run() {
478             InstanceIdentifier<T> tIID = removedIID.firstIdentifierOf(tClass);
479             if (tIID != null) {
480                 Optional<T> optional = mdsalUtils.read(datastoreType, tIID);
481                 if (optional.isPresent()) {
482
483                     if (shouldClean(optional.get())) {
484                         if (mdsalUtils.delete(datastoreType, tIID)) {
485                             postRemove(tIID);
486                         } else {
487                             removeFailed(tIID);
488                         }
489                     }
490
491                 }
492             } else {
493                 logger.error("Expected to find InstanceIdentifier<{}> but was not found: {}", tClass.getSimpleName(),
494                         removedIID);
495             }
496         }
497
498         /**
499          * If returns true the object will be removed from the datastore
500          *
501          * @param object
502          *         The object that might be removed.
503          * @return true if it should be removed.
504          */
505         abstract boolean shouldClean(final T object);
506
507         /**
508          * Called after an instance is removed.
509          *
510          * @param tIID
511          *         the InstanceIdentifier of the removed object
512          */
513         void postRemove(InstanceIdentifier<T> tIID) {
514
515         }
516
517         void removeFailed(InstanceIdentifier<T> tIID) {
518             logger.error("Failed to remove {}", tIID);
519         }
520     }
521
522
523     /**
524      * Listener for the packetcable:ccaps tree
525      */
526     private class CcapsDataChangeListener extends AbstractDataChangeListener<Ccap> {
527
528         private final DataValidator ccapsDataValidator = new DataValidator(new CcapsValidatorProviderFactory().build());
529
530         private final Set<InstanceIdentifier<Ccap>> updateQueue = Sets.newConcurrentHashSet();
531
532         public CcapsDataChangeListener() {
533             super(Ccap.class);
534         }
535
536         @Override
537         protected void handleCreatedData(final Map<InstanceIdentifier<Ccap>, Ccap> createdCcaps) {
538             if (createdCcaps.isEmpty()) {
539                 return;
540             }
541
542             final Map<InstanceIdentifier<Ccap>, ValidationException> errorMap =
543                     ccapsDataValidator.validateOneType(createdCcaps, Validator.Extent.NODE_AND_SUBTREE);
544
545             // validate all new objects an update operational datastore
546             if (!errorMap.isEmpty()) {
547                 // bad data write errors to operational datastore
548                 saveErrors(errorMap, createdCcaps);
549             }
550
551             if (createdCcaps.size() > errorMap.size()) {
552                 final Map<InstanceIdentifier<Ccap>, Ccap> goodData =
553                         Maps.newHashMapWithExpectedSize(createdCcaps.size() - errorMap.size());
554                 for (InstanceIdentifier<Ccap> iid : createdCcaps.keySet()) {
555                     if (!errorMap.containsKey(iid)) {
556                         goodData.put(iid, createdCcaps.get(iid));
557                     }
558                 }
559                 addNewCcaps(goodData);
560             }
561         }
562
563         private void addNewCcaps(final Map<InstanceIdentifier<Ccap>, Ccap> goodData) {
564             for (InstanceIdentifier<Ccap> iid : goodData.keySet()) {
565                 final Ccap ccap = goodData.get(iid);
566
567                 // add service
568                 if (pcmmServiceMap.containsKey(ccap.getCcapId())) {
569                     logger.error("Already monitoring CCAP - " + ccap);
570                     continue;
571                 }
572                 final PCMMService pcmmService = new PCMMService(IPCMMClient.CLIENT_TYPE, ccap);
573                 // TODO - may want to use the AMID but for the client type but probably not???
574 /*
575                             final PCMMService pcmmService = new PCMMService(
576                                     thisCcap.getAmId().getAmType().shortValue(), thisCcap);
577 */
578                 ConnectionBuilder connectionBuilder = new ConnectionBuilder();
579                 String message = pcmmService.addCcap();
580                 if (message.contains("200 OK")) {
581                     pcmmServiceMap.put(ccap.getCcapId(), pcmmService);
582                     ccapMap.put(ccap.getCcapId(), ccap);
583                     updateCcapMaps(ccap);
584                     logger.info("Created CCAP: {}/{} : {}", iid, ccap, message);
585                     logger.info("Created CCAP: {} : {}", iid, message);
586
587                     connectionBuilder.setConnected(true).setError(Collections.<String>emptyList());
588                 } else {
589                     logger.error("Create CCAP Failed: {} : {}", iid, message);
590
591                     connectionBuilder.setConnected(false).setError(Collections.singletonList(message));
592                 }
593
594                 //register rpc
595                 logger.info("Registering CCAP Routed RPC Path...");
596                 rpcRegistration.registerPath(CcapContext.class, iid);
597
598                 Optional<Ccap> optionalCcap = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, iid);
599
600                 final CcapBuilder responseCcapBuilder;
601                 if (optionalCcap.isPresent()) {
602                     responseCcapBuilder = new CcapBuilder(optionalCcap.get());
603                 } else {
604                     responseCcapBuilder = new CcapBuilder();
605                     responseCcapBuilder.setCcapId(ccap.getCcapId());
606                 }
607
608                 responseCcapBuilder.setConnection(connectionBuilder.build());
609
610                 mdsalUtils.put(LogicalDatastoreType.OPERATIONAL, iid, responseCcapBuilder.build());
611             }
612
613         }
614
615         @Override
616         protected void handleUpdatedData(final Map<InstanceIdentifier<Ccap>, Ccap> updatedCcaps,
617                                          final Map<InstanceIdentifier<Ccap>, Ccap> originalCcaps) {
618
619             // TODO actually support updates
620
621             // update operation not allowed -- restore the original config object and complain
622             for (final Map.Entry<InstanceIdentifier<Ccap>, Ccap> entry : updatedCcaps.entrySet()) {
623                 if (!originalCcaps.containsKey(entry.getKey())) {
624                     logger.error("No original data found for supposedly updated data: {}", entry.getValue());
625                     continue;
626                 }
627
628                 // If this notification is coming from our modification ignore it.
629                 if (updateQueue.contains(entry.getKey())) {
630                     updateQueue.remove(entry.getKey());
631                     continue;
632                 }
633
634                 final Ccap originalCcap = originalCcaps.get(entry.getKey());
635                 //final Ccap updatedCcap = entry.getValue();
636
637                 //register rpc
638                 logger.info("Registering CCAP Routed RPC Path...");
639                 rpcRegistration.registerPath(CcapContext.class, entry.getKey());
640
641                 // restore the original data
642                 updateQueue.add(entry.getKey());
643                 mdsalUtils.put(LogicalDatastoreType.CONFIGURATION, entry.getKey(), originalCcap);
644                 logger.error("CCAP update not permitted {}", entry.getKey());
645             }
646         }
647
648         @Override
649         protected void handleRemovedData(final Set<InstanceIdentifier<Ccap>> removedCcapPaths,
650                                          final Map<InstanceIdentifier<Ccap>, Ccap> originalCcaps) {
651
652             for (InstanceIdentifier<Ccap> iid : removedCcapPaths) {
653                 final Ccap nukedCcap = originalCcaps.get(iid);
654                 removeCcapFromAllMaps(nukedCcap);
655
656                 //unregister ccap rpc path
657                 logger.info("Un-Registering CCAP Routed RPC Path...");
658                 rpcRegistration.unregisterPath(CcapContext.class, iid);
659
660                 mdsalUtils.delete(LogicalDatastoreType.OPERATIONAL, iid);
661
662                 // clean up ccaps level if it is now empty
663                 executor.execute(new CcapsCleaner(iid));
664             }
665
666         }
667     }
668
669
670     private class QosDataChangeListener extends AbstractDataChangeListener<Gate> {
671
672         private final DataValidator qosDataValidator = new DataValidator(new QosValidatorProviderFactory().build());
673         private final Set<InstanceIdentifier<Gate>> updateQueue = Sets.newConcurrentHashSet();
674
675         public QosDataChangeListener() {
676             super(Gate.class);
677         }
678
679         @Override
680         protected void handleCreatedData(final Map<InstanceIdentifier<Gate>, Gate> createdData) {
681
682             final Map<InstanceIdentifier<Gate>, ValidationException> errorMap =
683                     qosDataValidator.validateOneType(createdData, Validator.Extent.NODE_AND_SUBTREE);
684
685             // validate all new objects an update operational datastore
686             if (!errorMap.isEmpty()) {
687                 // bad data write errors to operational datastore
688                 saveErrors(errorMap, createdData);
689             }
690
691             if (createdData.size() > errorMap.size()) {
692                 final Map<InstanceIdentifier<Gate>, Gate> goodData =
693                         Maps.newHashMapWithExpectedSize(createdData.size() - errorMap.size());
694                 for (InstanceIdentifier<Gate> iid : createdData.keySet()) {
695                     if (!errorMap.containsKey(iid)) {
696                         goodData.put(iid, createdData.get(iid));
697                     }
698                 }
699                 addNewGates(goodData);
700             }
701
702         }
703
704         private void addNewGates(final Map<InstanceIdentifier<Gate>, Gate> createdGates) {
705
706             for (InstanceIdentifier<Gate> gateIID : createdGates.keySet()) {
707                 final Gate newGate = createdGates.get(gateIID);
708
709                 final String newGatePathStr = makeGatePathString(gateIID);
710
711                 // if a new app comes along add RPC registration
712                 final InstanceIdentifier<App> appIID = gateIID.firstIdentifierOf(App.class);
713                 // TBD verify if App ID exists first
714
715                 //register appID RPC path
716                 logger.info("Registering App Routed RPC Path...");
717                 rpcRegistration.registerPath(AppContext.class, appIID);
718
719                 final InstanceIdentifier<Subscriber> subscriberIID = gateIID.firstIdentifierOf(Subscriber.class);
720                 final SubscriberKey subscriberKey = InstanceIdentifier.keyOf(subscriberIID);
721                 final InetAddress subscriberAddr = getInetAddress(subscriberKey.getSubscriberId());
722                 if (subscriberAddr == null) {
723                     final String msg = String.format("subscriberId must be a valid ipaddress: %s",
724                             subscriberKey.getSubscriberId());
725                     logger.error(msg);
726                     saveGateError(gateIID, newGatePathStr, msg);
727                     continue;
728                 }
729
730                 final Ccap ccap = findCcapForSubscriberId(subscriberAddr);
731                 if (ccap == null) {
732                     final String msg = String.format("Unable to find Ccap for subscriber %s: @ %s",
733                             subscriberKey.getSubscriberId(), newGatePathStr);
734                     logger.error(msg);
735                     saveGateError(gateIID, newGatePathStr, msg);
736                     continue;
737                 }
738
739                 final PCMMService pcmmService = pcmmServiceMap.get(ccap.getCcapId());
740                 if (pcmmService == null) {
741                     final String msg =
742                             String.format("Unable to locate PCMM Service for CCAP: %s ; with subscriber: %s", ccap,
743                                     subscriberKey.getSubscriberId());
744                     logger.error(msg);
745                     saveGateError(gateIID, newGatePathStr, msg);
746                     continue;
747                 }
748
749                 //
750                 // set up gate builder with known fields (and some empty ones)
751                 //
752                 final GateBuilder gateBuilder = new GateBuilder();
753                 gateBuilder.setGateId(newGate.getGateId())
754                         .setGatePath(newGatePathStr)
755                         .setCcapId(ccap.getCcapId())
756                         .setTrafficProfile(newGate.getTrafficProfile())
757                         .setClassifiers(newGate.getClassifiers())
758                         .setGateSpec(newGate.getGateSpec())
759                         .setCopsGateState("")
760                         .setCopsGateTimeInfo("")
761                         .setCopsGateUsageInfo("");
762
763                 //
764                 // Right now only ServiceClassName traffic Profile is supported. This logic needs to
765                 // be updated when the yang traffic-profile is extended to support new types
766                 // Override requested Direction using the Ccap configuration information about SCNs and
767                 // their configured direction.
768                 //
769                 final ServiceClassName scn = newGate.getTrafficProfile().getServiceClassName();
770                 final ServiceFlowDirection scnDirection = findScnOnCcap(scn, ccap);
771                 if (scnDirection == null) {
772                     final String msg =
773                         String.format("SCN %s not found on CCAP %s for %s", scn, ccap.getCcapId(), newGatePathStr);
774                     logger.error(msg);
775                     saveGateError(gateIID, newGatePathStr, msg);
776                     continue;
777                 }
778                 
779                 //
780                 // since we may be modifying the contents of the original request GateSpec
781                 // to update flow direction (based on the ccap SCN configuration) we need to
782                 // rebuild the requested gate spec and replace the existing one in the gate builder
783                 //
784                 final GateSpecBuilder gateSpecBuilder = new GateSpecBuilder();
785                 gateSpecBuilder.setDirection(scnDirection);
786                 gateSpecBuilder.setDscpTosMask(newGate.getGateSpec().getDscpTosMask());
787                 gateSpecBuilder.setDscpTosOverwrite(newGate.getGateSpec().getDscpTosOverwrite());
788                 final GateSpec gateSpec = gateSpecBuilder.build();
789                 gateBuilder.setGateSpec(gateSpec);
790
791                 //
792                 // build the gate to be requested
793                 //
794                 gateBuilder.setTimestamp(getNowTimeStamp());
795
796                 final Gate requestGate = gateBuilder.build();
797
798                 //
799                 // send gate request to Ccap
800                 //
801                 PCMMService.GateSendStatus status =
802                         pcmmService.sendGateSet(newGatePathStr, subscriberAddr, requestGate);
803                 if (status.didSucceed()) {
804                     gateMap.put(newGatePathStr, requestGate);
805                     gateCcapMap.put(newGatePathStr, ccap.getCcapId());
806
807                     //
808                     // inquire as to the status, and implementation info of the requested gate
809                     //
810                     PCMMService.GateSendStatus infoStatus = pcmmService.sendGateInfo(newGatePathStr);
811
812                     if (infoStatus.didSucceed()) {
813                         //
814                         // update builder with info for operational storage
815                         //
816                         gateBuilder.setCopsGateState(
817                                 infoStatus.getCopsGateState() + "/" + infoStatus.getCopsGateStateReason())
818                                 .setCopsGateTimeInfo(infoStatus.getCopsGateTimeInfo())
819                                 .setCopsGateUsageInfo(infoStatus.getCopsGateUsageInfo())
820                                 .setCopsGateId(status.getCopsGateId());
821                     } else {
822                         List<String> errors = new ArrayList<>(2);
823
824                         // Keep GateSetErrors
825                         if (gateBuilder.getError() != null) {
826                             errors.addAll(gateBuilder.getError());
827                         }
828
829                         errors.add(infoStatus.getMessage());
830                         gateBuilder.setError(errors);
831                     }
832                 }
833                 else {
834                     gateBuilder.setError(Collections.singletonList(status.getMessage()));
835                 }
836
837                 Gate operationalGate = gateBuilder.build();
838
839                 mdsalUtils.put(LogicalDatastoreType.OPERATIONAL, gateIID, operationalGate);
840             }
841
842         }
843
844         private void saveGateError(@Nonnull final InstanceIdentifier<Gate> gateIID, @Nonnull final String gatePathStr,
845                                    @Nonnull final String error) {
846             checkNotNull(gateIID);
847             checkNotNull(error);
848
849             final GateBuilder gateBuilder = new GateBuilder();
850             gateBuilder.setGateId(InstanceIdentifier.keyOf(gateIID).getGateId())
851                     .setGatePath(gatePathStr)
852                     .setCopsGateId("")
853                     .setCopsGateState("N/A");
854
855             gateBuilder.setError(Collections.singletonList(error));
856
857             Gate operationalGate = gateBuilder.build();
858
859             mdsalUtils.put(LogicalDatastoreType.OPERATIONAL, gateIID, operationalGate);
860         }
861
862         @Override
863         protected void handleUpdatedData(final Map<InstanceIdentifier<Gate>, Gate> updatedData,
864                                          final Map<InstanceIdentifier<Gate>, Gate> originalData) {
865             // TODO actually support updates
866
867             // update operation not allowed -- restore the original config object and complain
868             for (final Map.Entry<InstanceIdentifier<Gate>, Gate> entry : updatedData.entrySet()) {
869                 if (!originalData.containsKey(entry.getKey())) {
870                     logger.error("No original data found for supposedly updated data: {}", entry.getValue());
871                     continue;
872                 }
873
874                 // If this notification is coming from our modification ignore it.
875                 if (updateQueue.contains(entry.getKey())) {
876                     updateQueue.remove(entry.getKey());
877                     continue;
878                 }
879
880                 final Gate originalGate = originalData.get(entry.getKey());
881
882                 // restores the original data
883                 updateQueue.add(entry.getKey());
884                 mdsalUtils.put(LogicalDatastoreType.CONFIGURATION, entry.getKey(), originalGate);
885                 logger.error("Update not permitted {}", entry.getKey());
886
887             }
888         }
889
890
891
892         @Override
893         protected void handleRemovedData(final Set<InstanceIdentifier<Gate>> removedPaths,
894                                          final Map<InstanceIdentifier<Gate>, Gate> originalData) {
895
896             for (final InstanceIdentifier<Gate> removedGateIID : removedPaths) {
897
898                 mdsalUtils.delete(LogicalDatastoreType.OPERATIONAL, removedGateIID);
899
900                 executor.execute(new SubscriberCleaner(removedGateIID));
901
902                 final String gatePathStr = makeGatePathString(removedGateIID);
903
904                 if (gateMap.containsKey(gatePathStr)) {
905                     final Gate thisGate = gateMap.remove(gatePathStr);
906                     final String gateId = thisGate.getGateId();
907                     final String ccapId = gateCcapMap.remove(gatePathStr);
908                     final Ccap thisCcap = ccapMap.get(ccapId);
909                     final PCMMService service = pcmmServiceMap.get(thisCcap.getCcapId());
910                     if (service != null) {
911                         service.sendGateDelete(gatePathStr);
912                         logger.info("onDataChanged(): removed QoS gate {} for {}/{}/{}: ", gateId, ccapId, gatePathStr,thisGate);
913                     } else {
914                         logger.warn("Unable to send to locate PCMMService to send gate delete message with CCAP - "
915                                 + thisCcap);
916                     }
917                 }
918
919
920             }
921
922         }
923
924         private String makeGatePathString(InstanceIdentifier<Gate> iid) {
925             final InstanceIdentifier<App> appIID = iid.firstIdentifierOf(App.class);
926             final AppKey appKey = InstanceIdentifier.keyOf(appIID);
927
928             final InstanceIdentifier<Subscriber> subscriberIID = iid.firstIdentifierOf(Subscriber.class);
929             final SubscriberKey subscriberKey = InstanceIdentifier.keyOf(subscriberIID);
930
931             final GateKey gateKey = InstanceIdentifier.keyOf(iid);
932
933             return appKey.getAppId() + "/" + subscriberKey.getSubscriberId() + "/" + gateKey.getGateId();
934         }
935     }
936
937
938     @Override
939     public Future<RpcResult<CcapSetConnectionOutput>> ccapSetConnection(CcapSetConnectionInput input) {
940         // TODO refactor this method into smaller parts
941
942         InstanceIdentifier<Ccap> ccapIid = (InstanceIdentifier<Ccap>) input.getCcapId();
943         List<String> outputError = new ArrayList<String>();
944         String rpcResponse = null;
945         Boolean inputIsConnected = input.getConnection().isConnected();
946         Boolean effectiveIsConnected = null;
947         String ccapId = input.getCcapId().firstIdentifierOf(Ccap.class).firstKeyOf(Ccap.class).getCcapId();
948         PCMMService pcmmService = pcmmServiceMap.get(ccapId);
949
950         if (!inputIsConnected) {
951             // set connected false
952             if (pcmmService.getPcmmPdpSocket()) {
953                 outputError.add(ccapId + ": CCAP COPS socket is already closed");
954                 effectiveIsConnected = false;
955             } else {
956                 //if (!pcmmService.getPcmmCcapClientIsConnected()) {
957                 outputError.add(ccapId + ": CCAP client is disconnected with error: "
958                         + pcmmService.getPcmmCcapClientConnectErrMsg());
959                 //}
960                 pcmmService.ccapClient.disconnect();
961                 effectiveIsConnected = false;
962             }
963         } else {
964             // set connected true
965             if (!pcmmService.getPcmmPdpSocket() && pcmmService.getPcmmCcapClientIsConnected()) {
966                 outputError.add(ccapId + ": CCAP COPS socket is already open");
967                 outputError.add(ccapId + ": CCAP client is connected");
968                 effectiveIsConnected = true;
969             } else {
970                 if (pcmmService.getPcmmCcapClientIsConnected()) {
971                     pcmmService.ccapClient.disconnect();
972                 }
973                 pcmmService.ccapClient.connect();
974                 if (pcmmService.getPcmmCcapClientIsConnected()) {
975                     effectiveIsConnected = true;
976                     outputError.add(ccapId + ": CCAP client is connected");
977                 } else {
978                     effectiveIsConnected = false;
979                     outputError.add(ccapId + ": CCAP client is disconnected with error: "
980                             + pcmmService.getPcmmCcapClientConnectErrMsg());
981                 }
982             }
983         }
984
985         DateAndTime connectionDateAndTime = getNowTimeStamp();
986         org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.ccap.set.connection.output.ccap.ConnectionBuilder
987                 connectionRpcOutput =
988                 new org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.ccap.set.connection.output.ccap.ConnectionBuilder()
989                         .setConnected(effectiveIsConnected)
990                         .setError(outputError)
991                         .setTimestamp(connectionDateAndTime);
992
993         org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.ccap.set.connection.output.CcapBuilder ccapRpcOutput =
994                 new org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.ccap.set.connection.output.CcapBuilder().setCcapId(
995                         ccapId).setConnection(connectionRpcOutput.build());
996
997
998         ConnectionBuilder connectionOps = new ConnectionBuilder().setConnected(effectiveIsConnected)
999                 .setError(outputError)
1000                 .setTimestamp(connectionDateAndTime);
1001
1002         CcapBuilder responseCcapBuilder = new CcapBuilder().setCcapId(ccapId).setConnection(connectionOps.build());
1003
1004
1005         mdsalUtils.put(LogicalDatastoreType.OPERATIONAL, ccapIid, responseCcapBuilder.build());
1006
1007
1008         DateAndTime rpcDateAndTime = getNowTimeStamp();
1009         rpcResponse = ccapId + ": CCAP set complete";
1010         CcapSetConnectionOutputBuilder outputBuilder =
1011                 new CcapSetConnectionOutputBuilder().setCcap(ccapRpcOutput.build())
1012                         .setResponse(rpcResponse)
1013                         .setTimestamp(rpcDateAndTime);
1014
1015         return Futures.immediateFuture(RpcResultBuilder.success(outputBuilder.build()).build());
1016     }
1017
1018
1019
1020     @Override
1021     public Future<RpcResult<CcapPollConnectionOutput>> ccapPollConnection(CcapPollConnectionInput input) {
1022         // TODO refactor this method into smaller parts
1023
1024         InstanceIdentifier<Ccap> ccapIid = (InstanceIdentifier<Ccap>) input.getCcapId();
1025         List<String> outputError = new ArrayList<String>();
1026
1027         String ccapId = input.getCcapId().firstIdentifierOf(Ccap.class).firstKeyOf(Ccap.class).getCcapId();
1028         PCMMService pcmmService = pcmmServiceMap.get(ccapId);
1029         Boolean effectiveIsConnected = true;
1030         String response = null;
1031         org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.ccap.poll.connection.output.ccap.ConnectionBuilder
1032                 connectionRpcOutput =
1033                 new org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.ccap.poll.connection.output.ccap.ConnectionBuilder();
1034
1035         if (pcmmService != null) {
1036             if (pcmmService.getPcmmPdpSocket()) {
1037                 outputError.add(ccapId + ": CCAP Cops socket is closed");
1038                 if (!pcmmService.getPcmmCcapClientIsConnected()) {
1039                     outputError.add(ccapId + ": CCAP client is disconnected with error: "
1040                             + pcmmService.getPcmmCcapClientConnectErrMsg());
1041                 }
1042                 effectiveIsConnected = false;
1043             } else {
1044                 //outputError.add(String.format(ccapId+": CCAP Cops socket is open"));
1045                 if (!pcmmService.getPcmmCcapClientIsConnected()) {
1046                     outputError.add(ccapId + ": CCAP client is disconnected with error: "
1047                             + pcmmService.getPcmmCcapClientConnectErrMsg());
1048                     effectiveIsConnected = false;
1049                 } else {
1050                     outputError.add(ccapId + ": CCAP client is connected");
1051                 }
1052             }
1053             DateAndTime connectionDateAndTime = getNowTimeStamp();
1054
1055
1056             ConnectionBuilder connectionOps = new ConnectionBuilder().setConnected(effectiveIsConnected)
1057                     .setError(outputError)
1058                     .setTimestamp(connectionDateAndTime);
1059
1060             CcapBuilder responseCcapBuilder = new CcapBuilder().setCcapId(ccapId).setConnection(connectionOps.build());
1061
1062             connectionRpcOutput =
1063                     new org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.ccap.poll.connection.output.ccap.ConnectionBuilder()
1064                             .setConnected(effectiveIsConnected)
1065                             .setError(outputError)
1066                             .setTimestamp(connectionDateAndTime);
1067
1068             mdsalUtils.put(LogicalDatastoreType.OPERATIONAL, ccapIid, responseCcapBuilder.build());
1069             response = ccapId + ": CCAP poll complete";
1070         } else {
1071             //pcmmService is null, do not poll
1072             response = ccapId + ": CCAP connection null; no poll performed";
1073         }
1074
1075         DateAndTime rpcDateAndTime = getNowTimeStamp();
1076
1077         org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.ccap.poll.connection.output.CcapBuilder ccapRpcOutput =
1078                 new org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.ccap.poll.connection.output.CcapBuilder().setCcapId(
1079                         ccapId).setConnection(connectionRpcOutput.build());
1080
1081         CcapPollConnectionOutputBuilder outputBuilder =
1082                 new CcapPollConnectionOutputBuilder().setCcap(ccapRpcOutput.build())
1083                         .setResponse(response)
1084                         .setTimestamp(rpcDateAndTime);
1085
1086         return Futures.immediateFuture(RpcResultBuilder.success(outputBuilder.build()).build());
1087     }
1088
1089
1090
1091     private App readAppFromOperationalDatastore(InstanceIdentifier<App> appIid) {
1092         Optional<App> optionalApp = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, appIid);
1093         AppBuilder thisAppBuilder = new AppBuilder(optionalApp.get());
1094         App thisApp = thisAppBuilder.build();
1095         logger.info("readAppFromConfigDatastore() retrived App: " + thisApp.getAppId());
1096         return thisApp;
1097     }
1098
1099     private Gate readGateFromOperationalDatastore(InstanceIdentifier<Gate> gateIid) {
1100         Optional<Gate> optionalGate = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, gateIid);
1101         if (optionalGate.isPresent()) {
1102             GateBuilder gateBuilder = new GateBuilder(optionalGate.get());
1103             Gate thisGate = gateBuilder.build();
1104             return thisGate;
1105         } else {
1106             return null;
1107         }
1108     }
1109
1110     private Subscriber readSubscriberFromOperationalDatastore(InstanceIdentifier<Subscriber> subscriberIid) {
1111         Optional<Subscriber> optionalSubscriber = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, subscriberIid);
1112         if (optionalSubscriber.isPresent()) {
1113             SubscriberBuilder subscriberBuilder = new SubscriberBuilder(optionalSubscriber.get());
1114             Subscriber thisSubscriber = subscriberBuilder.build();
1115             return thisSubscriber;
1116         } else {
1117             return null;
1118         }
1119     }
1120
1121
1122
1123     @Override
1124     public Future<RpcResult<QosPollGatesOutput>> qosPollGates(QosPollGatesInput input) {
1125         // TODO refactor this method into smaller parts
1126
1127         InstanceIdentifier<App> appIid = (InstanceIdentifier<App>) input.getAppId();
1128         //logger.info("qospollgates appIid : "+appIid.toString());
1129         App app = readAppFromOperationalDatastore(appIid);
1130         //logger.info("qospollgates app : "+app.toString());
1131         AppKey appKey = InstanceIdentifier.keyOf(appIid);
1132         String inputSubscriberId = input.getSubscriberId();
1133         String inputGateId = input.getGateId();
1134         List<String> gateOutputError = Collections.emptyList();
1135         String subscriberId = null;
1136         String gateId = null;
1137         String ccapId = null;
1138         String gatePathStr = null;
1139         String opsCopsGateId = null;
1140         Gate opsGate = null;
1141
1142         String rpcResponse = null;
1143
1144         org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.qos.poll.gates.output.GateBuilder gateOutputBuilder =
1145                 new org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.qos.poll.gates.output.GateBuilder();
1146
1147         GateBuilder gateBuilder = new GateBuilder();
1148
1149         if (inputSubscriberId != null) {
1150             if (inputGateId != null) {
1151                 //Subscriber Id and Gate Id provided, only one gate to be poolled
1152
1153                 //generate the gateiid
1154                 InstanceIdentifier<Gate> gateIid = appIid.builder()
1155                         .child(Subscribers.class)
1156                         .child(Subscriber.class, new SubscriberKey(inputSubscriberId))
1157                         .child(Gates.class)
1158                         .child(Gate.class, new GateKey(inputGateId))
1159                         .build();
1160
1161
1162                 opsGate = readGateFromOperationalDatastore(gateIid);
1163
1164                 //does the gate exists in the Operational DS?
1165                 if (opsGate == null) {
1166                     gatePathStr = appKey.getAppId() + "/" + inputSubscriberId + "/" + inputGateId;
1167                     rpcResponse = gatePathStr + ": gate does not exist in the system; gate poll not performed";
1168                 } else {
1169                     opsCopsGateId = opsGate.getCopsGateId();
1170                     gatePathStr = opsGate.getGatePath();
1171
1172                     if ((!Objects.equals(opsCopsGateId, "")) && (!Objects.equals(opsCopsGateId, null))) {
1173                         ccapId = findCcapForSubscriberId(getInetAddress(inputSubscriberId)).getCcapId();
1174                         PCMMService pcmmService = pcmmServiceMap.get(ccapId);
1175                         //is the CCAP socket open?
1176                         if (!pcmmService.getPcmmPdpSocket() && pcmmService.getPcmmCcapClientIsConnected()) {
1177                             PCMMService.GateSendStatus status = pcmmService.sendGateInfo(gatePathStr);
1178                             DateAndTime gateDateAndTime = getNowTimeStamp();
1179                             //logger.info("qospollgates Gate Status : GateID/"+status.getCopsGateId());
1180                             //logger.info("qospollgates Gate Status : Message/"+status.getMessage());
1181                             //logger.info("qospollgates Gate Status : DidSucceed/"+status.didSucceed());
1182                             gateOutputError = Collections.singletonList(status.getMessage());
1183
1184                             gateOutputBuilder.setGatePath(gatePathStr)
1185                                     .setCcapId(ccapId)
1186                                     .setCopsGateState(status.getCopsGateState() + "/" + status.getCopsGateStateReason())
1187                                     .setCopsGateTimeInfo(status.getCopsGateTimeInfo())
1188                                     .setCopsGateUsageInfo(status.getCopsGateUsageInfo())
1189                                     .setCopsGateId(status.getCopsGateId())
1190                                     .setError(gateOutputError)
1191                                     .setTimestamp(gateDateAndTime);
1192
1193                             gateBuilder.setGateId(inputGateId)
1194                                     .setGatePath(gatePathStr)
1195                                     .setCcapId(ccapId)
1196                                     .setCopsGateState(status.getCopsGateState() + "/" + status.getCopsGateStateReason())
1197                                     .setCopsGateTimeInfo(status.getCopsGateTimeInfo())
1198                                     .setCopsGateUsageInfo(status.getCopsGateUsageInfo())
1199                                     .setCopsGateId(status.getCopsGateId())
1200                                     .setError(gateOutputError)
1201                                     .setTimestamp(gateDateAndTime);
1202
1203                             mdsalUtils.put(LogicalDatastoreType.OPERATIONAL, gateIid, gateBuilder.build());
1204                             rpcResponse = gatePathStr + ": gate poll complete";
1205                         } else {
1206                             rpcResponse =
1207                                     ccapId + ": CCAP socket is down or client disconnected; gate poll not performed";
1208                         }
1209                     } else {
1210                         rpcResponse = gatePathStr + ": gate not active; gate poll not performed";
1211                     }
1212                 }
1213             } else {
1214                 //inputGateId is null; pool all gates for the subscriber if the sub exists
1215
1216                 //generate active subIid
1217                 InstanceIdentifier<Subscriber> subIid = appIid.builder()
1218                         .child(Subscribers.class)
1219                         .child(Subscriber.class, new SubscriberKey(inputSubscriberId))
1220                         .build();
1221                 //does the subscriber provided exists in the Operational Datastore?
1222                 Subscriber sub = readSubscriberFromOperationalDatastore(subIid);
1223                 if (sub != null) {
1224                     //If Subscriber exsits poll all gates for the subscriber
1225                     subscriberId = sub.getSubscriberId();
1226                     List<Gate> gateList = sub.getGates().getGate();
1227                     for (Gate gate : gateList) {
1228                         //generate active gateIid
1229                         gateId = gate.getGateId();
1230                         InstanceIdentifier<Gate> gateIid =
1231                                 subIid.builder().child(Gates.class).child(Gate.class, new GateKey(gateId)).build();
1232
1233                         opsGate = readGateFromOperationalDatastore(gateIid);
1234                         opsCopsGateId = opsGate.getCopsGateId();
1235                         //generate active gatePathStr
1236                         gatePathStr = appKey.getAppId() + "/" + subscriberId + "/" + gateId;
1237
1238                         if ((!Objects.equals(opsCopsGateId, "")) && (!Objects.equals(opsCopsGateId, null))) {
1239                             ccapId = findCcapForSubscriberId(getInetAddress(subscriberId)).getCcapId();
1240                             PCMMService pcmmService = pcmmServiceMap.get(ccapId);
1241                             //is the CCAP socket open?
1242                             if (!pcmmService.getPcmmPdpSocket() && pcmmService.getPcmmCcapClientIsConnected()) {
1243                                 PCMMService.GateSendStatus status = pcmmService.sendGateInfo(gatePathStr);
1244                                 DateAndTime gateDateAndTime = getNowTimeStamp();
1245
1246                                 gateBuilder.setGateId(gateId)
1247                                         .setGatePath(gatePathStr)
1248                                         .setCcapId(ccapId)
1249                                         .setCopsGateState(
1250                                                 status.getCopsGateState() + "/" + status.getCopsGateStateReason())
1251                                         .setCopsGateTimeInfo(status.getCopsGateTimeInfo())
1252                                         .setCopsGateUsageInfo(status.getCopsGateUsageInfo())
1253                                         .setCopsGateId(status.getCopsGateId())
1254                                         .setError(gateOutputError)
1255                                         .setTimestamp(gateDateAndTime);
1256
1257                                 mdsalUtils.put(LogicalDatastoreType.OPERATIONAL, gateIid, gateBuilder.build());
1258                             } else {
1259                                 logger.info(
1260                                         "qospollgates: {}: CCAP Cops socket is down or client disconnected; gate poll not performed",
1261                                         ccapId);
1262                             }
1263                         } else {
1264                             //TODO define what happens if a gate is not active.. is nothing ok?
1265                             logger.info("qospollgates: {}: gate not active; gate poll not performed", gatePathStr);
1266                         }
1267                     } //for
1268                     rpcResponse = inputSubscriberId + "/: subscriber subtree poll in progress";
1269                 } else {
1270                     rpcResponse =
1271                             inputSubscriberId + "/: subscriber is not defined in the system, gate poll not performed";
1272                 }
1273             }
1274         } //inputSubId if
1275         else {
1276             // inputSubId is null
1277             if (inputGateId != null) {
1278                 gatePathStr = appKey.getAppId() + "/" + inputSubscriberId + "/" + inputGateId;
1279                 rpcResponse = gatePathStr + ": Subscriber ID not provided; gate poll not performed";
1280             } else {
1281                 //poll all gates for the appId
1282                 PollAllGatesForApp pollAllGatesForApp = new PollAllGatesForApp(appIid,app);
1283                 Thread t = new Thread(pollAllGatesForApp);
1284                 t.start();
1285                 rpcResponse = appKey.getAppId() + "/: gate subtree poll in progress";
1286             }
1287         }
1288
1289         DateAndTime rpcDateAndTime = getNowTimeStamp();
1290
1291         QosPollGatesOutputBuilder outputBuilder = new QosPollGatesOutputBuilder().setTimestamp(rpcDateAndTime)
1292                 .setResponse(rpcResponse)
1293                 .setGate(gateOutputBuilder.build());
1294         return Futures.immediateFuture(RpcResultBuilder.success(outputBuilder.build()).build());
1295     }
1296     private class PollAllGatesForApp implements Runnable {
1297
1298         private InstanceIdentifier <App> appIid;
1299         private App app;
1300
1301         private PollAllGatesForApp (InstanceIdentifier <App> appIid, App app) {
1302             this.app = app;
1303             this.appIid = appIid;
1304         }
1305
1306         @Override
1307         public void run() {
1308
1309             org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.qos.poll.gates.output.GateBuilder gateOutputBuilder =
1310                     new org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.qos.poll.gates.output.GateBuilder();
1311
1312             GateBuilder gateBuilder = new GateBuilder();
1313
1314             //generate appKey
1315             AppKey appKey = InstanceIdentifier.keyOf(appIid);
1316
1317             Subscribers subs = app.getSubscribers();
1318             logger.info("qospollgates subscribers: " + subs.toString());
1319
1320             List<Subscriber> subList = subs.getSubscriber();
1321             logger.info("qospollgates subList: " + subList.toString());
1322
1323             for (Subscriber sub : subList) {
1324                 //generate active subIid
1325                 String subscriberId = sub.getSubscriberId();
1326                 InstanceIdentifier<Subscriber> subIid = appIid.builder()
1327                         .child(Subscribers.class)
1328                         .child(Subscriber.class, new SubscriberKey(subscriberId))
1329                         .build();
1330
1331                 List<Gate> gateList = sub.getGates().getGate();
1332
1333                 for (Gate gate : gateList) {
1334                     //logger.info("qospollgates active gate: "+gate);
1335
1336                     //generate active gateIid
1337                     String gateId = gate.getGateId();
1338                     InstanceIdentifier<Gate> gateIid =
1339                             subIid.builder().child(Gates.class).child(Gate.class, new GateKey(gateId)).build();
1340
1341
1342                     Gate opsGate = readGateFromOperationalDatastore(gateIid);
1343                     String opsCopsGateId = opsGate.getCopsGateId();
1344                     //generate active gatePathStr
1345                     String gatePathStr = appKey.getAppId() + "/" + subscriberId + "/" + gateId;
1346
1347                     if ((!Objects.equals(opsCopsGateId, "")) && (!Objects.equals(opsCopsGateId, null))) {
1348                         String ccapId = findCcapForSubscriberId(getInetAddress(subscriberId)).getCcapId();
1349                         PCMMService pcmmService = pcmmServiceMap.get(ccapId);
1350                         //is the CCAP socket open?
1351                         if (!pcmmService.getPcmmPdpSocket() && pcmmService.getPcmmCcapClientIsConnected()) {
1352                             PCMMService.GateSendStatus status = pcmmService.sendGateInfo(gatePathStr);
1353                             DateAndTime gateDateAndTime = getNowTimeStamp();
1354                             List<String> gateOutputError = Collections.singletonList(status.getMessage());
1355
1356
1357                             gateBuilder.setGateId(gateId)
1358                                     .setGatePath(gatePathStr)
1359                                     .setCcapId(ccapId)
1360                                     .setCopsGateState(
1361                                             status.getCopsGateState() + "/" + status.getCopsGateStateReason())
1362                                     .setCopsGateTimeInfo(status.getCopsGateTimeInfo())
1363                                     .setCopsGateUsageInfo(status.getCopsGateUsageInfo())
1364                                     .setCopsGateId(status.getCopsGateId())
1365                                     .setError(gateOutputError)
1366                                     .setTimestamp(gateDateAndTime);
1367
1368                             mdsalUtils.put(LogicalDatastoreType.OPERATIONAL, gateIid, gateBuilder.build());
1369                         } else {
1370                             logger.info(
1371                                     "qospollgates: {}: CCAP socket is down or client disconnected; gate poll not performed",
1372                                     ccapId);
1373                         }
1374                     } else {
1375                         //TODO define what happens if a gate is not active.. is nothing ok
1376                         logger.info("qospollgates: {}: gate not active; gate poll not performed", gatePathStr);
1377                     }
1378                 }
1379             }
1380         }
1381
1382     }
1383
1384
1385     private DateAndTime getNowTimeStamp() {
1386         DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX");
1387         return new DateAndTime(dateFormat.format(new Date()));
1388     }
1389 }