Merge changes I4ba7cf7d,Ia3e319ee
[controller.git] / opendaylight / md-sal / forwardingrules-manager / src / main / java / org / opendaylight / controller / forwardingrulesmanager / consumer / impl / MeterConsumerImpl.java
1 package org.opendaylight.controller.forwardingrulesmanager.consumer.impl;
2
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.EnumSet;
6 import java.util.HashMap;
7 import java.util.HashSet;
8 import java.util.Iterator;
9 import java.util.List;
10 import java.util.Map;
11 import java.util.Map.Entry;
12 import java.util.Set;
13 import java.util.concurrent.ConcurrentHashMap;
14 import java.util.concurrent.ConcurrentMap;
15
16 import org.opendaylight.controller.clustering.services.CacheConfigException;
17 import org.opendaylight.controller.clustering.services.CacheExistException;
18 import org.opendaylight.controller.clustering.services.IClusterContainerServices;
19 import org.opendaylight.controller.clustering.services.IClusterServices;
20 import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler;
21 import org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction;
22 import org.opendaylight.controller.md.sal.common.api.data.DataModification;
23 import org.opendaylight.controller.sal.common.util.Rpcs;
24 import org.opendaylight.controller.sal.core.IContainer;
25 import org.opendaylight.controller.sal.core.Node;
26 import org.opendaylight.controller.sal.utils.GlobalConstants;
27 import org.opendaylight.controller.sal.utils.Status;
28 import org.opendaylight.controller.sal.utils.StatusCode;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.config.rev131024.Meters;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.config.rev131024.meters.Meter;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.config.rev131024.meters.MeterKey;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.AddMeterInputBuilder;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.MeterAdded;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.MeterRemoved;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.MeterUpdated;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.SalMeterListener;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.SalMeterService;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.UpdateMeterInputBuilder;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.service.rev130918.meter.update.UpdatedMeterBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.band.type.BandType;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.band.type.band.type.Drop;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.band.type.band.type.DscpRemark;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.band.type.band.type.Experimenter;
44 import org.opendaylight.yangtools.concepts.Registration;
45 import org.opendaylight.yangtools.yang.binding.DataObject;
46 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
47 import org.opendaylight.yangtools.yang.binding.NotificationListener;
48 import org.opendaylight.yangtools.yang.common.RpcResult;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51
52 public class MeterConsumerImpl implements IForwardingRulesManager {
53     protected static final Logger logger = LoggerFactory.getLogger(MeterConsumerImpl.class);
54     private final MeterEventListener meterEventListener = new MeterEventListener();
55     private Registration<NotificationListener> meterListener;
56     private SalMeterService meterService;
57     private MeterDataCommitHandler commitHandler;
58
59     private ConcurrentMap<MeterKey, Meter> originalSwMeterView;
60     private ConcurrentMap<MeterKey, Meter> installedSwMeterView;
61
62     private ConcurrentMap<Node, List<Meter>> nodeMeters;
63     private ConcurrentMap<MeterKey, Meter> inactiveMeters;
64
65     private IClusterContainerServices clusterMeterContainerService = null;
66     private IContainer container;
67
68     public MeterConsumerImpl() {
69         InstanceIdentifier<? extends DataObject> path = InstanceIdentifier.builder(Meters.class).child(Meter.class)
70                 .toInstance();
71         meterService = FRMConsumerImpl.getProviderSession().getRpcService(SalMeterService.class);
72         clusterMeterContainerService = FRMConsumerImpl.getClusterContainerService();
73
74         container = FRMConsumerImpl.getContainer();
75
76         if (!(cacheStartup())) {
77             logger.error("Unable to allocate/retrieve meter cache");
78             System.out.println("Unable to allocate/retrieve meter cache");
79         }
80
81         if (null == meterService) {
82             logger.error("Consumer SAL Meter Service is down or NULL. FRM may not function as intended");
83             System.out.println("Consumer SAL Meter Service is down or NULL.");
84             return;
85         }
86
87         // For switch/plugin events
88         meterListener = FRMConsumerImpl.getNotificationService().registerNotificationListener(meterEventListener);
89
90         if (null == meterListener) {
91             logger.error("Listener to listen on meter data modifcation events");
92             System.out.println("Listener to listen on meter data modifcation events.");
93             return;
94         }
95
96         commitHandler = new MeterDataCommitHandler();
97         FRMConsumerImpl.getDataProviderService().registerCommitHandler(path, commitHandler);
98     }
99
100     private boolean allocateMeterCaches() {
101         if (this.clusterMeterContainerService == null) {
102             logger.warn("Meter: Un-initialized clusterMeterContainerService, can't create cache");
103             return false;
104         }
105
106         try {
107             clusterMeterContainerService.createCache("frm.originalSwMeterView",
108                     EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
109
110             clusterMeterContainerService.createCache("frm.installedSwMeterView",
111                     EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
112
113             clusterMeterContainerService.createCache("frm.inactiveMeters",
114                     EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
115
116             clusterMeterContainerService.createCache("frm.nodeMeters",
117                     EnumSet.of(IClusterServices.cacheMode.TRANSACTIONAL));
118
119             // TODO for cluster mode
120             /*
121              * clusterMeterContainerService.createCache(WORK_STATUS_CACHE,
122              * EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL,
123              * IClusterServices.cacheMode.ASYNC));
124              *
125              * clusterMeterContainerService.createCache(WORK_ORDER_CACHE,
126              * EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL,
127              * IClusterServices.cacheMode.ASYNC));
128              */
129
130         } catch (CacheConfigException cce) {
131             logger.error("Meter CacheConfigException");
132             return false;
133
134         } catch (CacheExistException cce) {
135             logger.error(" Meter CacheExistException");
136         }
137
138         return true;
139     }
140
141     private void nonClusterMeterObjectCreate() {
142         originalSwMeterView = new ConcurrentHashMap<MeterKey, Meter>();
143         installedSwMeterView = new ConcurrentHashMap<MeterKey, Meter>();
144         nodeMeters = new ConcurrentHashMap<Node, List<Meter>>();
145         inactiveMeters = new ConcurrentHashMap<MeterKey, Meter>();
146     }
147
148     @SuppressWarnings({ "unchecked" })
149     private boolean retrieveMeterCaches() {
150         ConcurrentMap<?, ?> map;
151
152         if (this.clusterMeterContainerService == null) {
153             logger.warn("Meter: un-initialized clusterMeterContainerService, can't retrieve cache");
154             nonClusterMeterObjectCreate();
155             return false;
156         }
157
158         map = clusterMeterContainerService.getCache("frm.originalSwMeterView");
159         if (map != null) {
160             originalSwMeterView = (ConcurrentMap<MeterKey, Meter>) map;
161         } else {
162             logger.error("Retrieval of cache(originalSwMeterView) failed");
163             return false;
164         }
165
166         map = clusterMeterContainerService.getCache("frm.installedSwMeterView");
167         if (map != null) {
168             installedSwMeterView = (ConcurrentMap<MeterKey, Meter>) map;
169         } else {
170             logger.error("Retrieval of cache(installedSwMeterView) failed");
171             return false;
172         }
173
174         map = clusterMeterContainerService.getCache("frm.inactiveMeters");
175         if (map != null) {
176             inactiveMeters = (ConcurrentMap<MeterKey, Meter>) map;
177         } else {
178             logger.error("Retrieval of cache(inactiveMeters) failed");
179             return false;
180         }
181
182         map = clusterMeterContainerService.getCache("frm.nodeMeters");
183         if (map != null) {
184             nodeMeters = (ConcurrentMap<Node, List<Meter>>) map;
185         } else {
186             logger.error("Retrieval of cache(nodeMeter) failed");
187             return false;
188         }
189
190         return true;
191     }
192
193     private boolean cacheStartup() {
194         if (allocateMeterCaches()) {
195             if (retrieveMeterCaches()) {
196                 return true;
197             }
198         }
199
200         return false;
201     }
202
203     /**
204      * Adds Meter to the southbound plugin and our internal database
205      *
206      * @param path
207      * @param dataObject
208      */
209     private Status addMeter(InstanceIdentifier<?> path, Meter meterAddDataObject) {
210         MeterKey meterKey = meterAddDataObject.getKey();
211
212         if (null != meterKey && validateMeter(meterAddDataObject, FRMUtil.operation.ADD).isSuccess()) {
213             if (meterAddDataObject.isInstall()) {
214                 AddMeterInputBuilder meterBuilder = new AddMeterInputBuilder();
215
216                 meterBuilder.setContainerName(meterAddDataObject.getContainerName());
217                 meterBuilder.setFlags(meterAddDataObject.getFlags());
218                 meterBuilder.setMeterBandHeaders(meterAddDataObject.getMeterBandHeaders());
219                 meterBuilder.setMeterId(meterAddDataObject.getMeterId());
220                 meterBuilder.setNode(meterAddDataObject.getNode());
221                 originalSwMeterView.put(meterKey, meterAddDataObject);
222                 meterService.addMeter(meterBuilder.build());
223             }
224
225             originalSwMeterView.put(meterKey, meterAddDataObject);
226         } else {
227             return new Status(StatusCode.BADREQUEST, "Meter Key or attribute validation failed");
228         }
229
230         return new Status(StatusCode.SUCCESS);
231     }
232
233     /*
234      * Update Meter to the southbound plugin and our internal database
235      *
236      * @param path
237      *
238      * @param dataObject
239      */
240     private Status updateMeter(InstanceIdentifier<?> path, Meter meterUpdateDataObject) {
241         MeterKey meterKey = meterUpdateDataObject.getKey();
242         UpdatedMeterBuilder updateMeterBuilder = null;
243
244         if (null != meterKey && validateMeter(meterUpdateDataObject, FRMUtil.operation.UPDATE).isSuccess()) {
245
246             if (originalSwMeterView.containsKey(meterKey)) {
247                 originalSwMeterView.remove(meterKey);
248                 originalSwMeterView.put(meterKey, meterUpdateDataObject);
249             }
250
251             if (meterUpdateDataObject.isInstall()) {
252                 UpdateMeterInputBuilder updateMeterInputBuilder = new UpdateMeterInputBuilder();
253                 updateMeterBuilder = new UpdatedMeterBuilder();
254                 updateMeterBuilder.fieldsFrom(meterUpdateDataObject);
255                 updateMeterInputBuilder.setUpdatedMeter(updateMeterBuilder.build());
256
257                 if (installedSwMeterView.containsKey(meterKey)) {
258                     installedSwMeterView.remove(meterKey);
259                     installedSwMeterView.put(meterKey, meterUpdateDataObject);
260                 }
261
262                 meterService.updateMeter(updateMeterInputBuilder.build());
263             }
264
265         } else {
266             return new Status(StatusCode.BADREQUEST, "Meter Key or attribute validation failed");
267         }
268
269         return new Status(StatusCode.SUCCESS);
270     }
271
272     /*
273      * Remove Meter to the southbound plugin and our internal database
274      *
275      * @param path
276      *
277      * @param dataObject
278      */
279     private Status RemoveMeter(InstanceIdentifier<?> path, Meter meterUpdateDataObject) {
280         MeterKey meterKey = meterUpdateDataObject.getKey();
281
282         if (null != meterKey && validateMeter(meterUpdateDataObject, FRMUtil.operation.ADD).isSuccess()) {
283             if (meterUpdateDataObject.isInstall()) {
284                 UpdateMeterInputBuilder updateMeterBuilder = new UpdateMeterInputBuilder();
285
286                 installedSwMeterView.put(meterKey, meterUpdateDataObject);
287                 meterService.updateMeter(updateMeterBuilder.build());
288             }
289
290             originalSwMeterView.put(meterKey, meterUpdateDataObject);
291         } else {
292             return new Status(StatusCode.BADREQUEST, "Meter Key or attribute validation failed");
293         }
294
295         return new Status(StatusCode.SUCCESS);
296     }
297
298     public Status validateMeter(Meter meter, FRMUtil.operation operation) {
299         String containerName;
300         String meterName;
301         Status returnStatus = null;
302         boolean returnResult;
303
304         if (null != meter) {
305             containerName = meter.getContainerName();
306
307             if (null == containerName) {
308                 containerName = GlobalConstants.DEFAULT.toString();
309             } else if (!FRMUtil.isNameValid(containerName)) {
310                 logger.error("Container Name is invalid %s" + containerName);
311                 returnStatus = new Status(StatusCode.BADREQUEST, "Container Name is invalid");
312                 return returnStatus;
313             }
314
315             meterName = meter.getMeterName();
316             if (!FRMUtil.isNameValid(meterName)) {
317                 logger.error("Meter Name is invalid %s" + meterName);
318                 returnStatus = new Status(StatusCode.BADREQUEST, "Meter Name is invalid");
319                 return returnStatus;
320             }
321
322             returnResult = doesMeterEntryExists(meter.getKey(), meterName, containerName);
323
324             if (FRMUtil.operation.ADD == operation && returnResult) {
325                 logger.error("Record with same Meter Name exists");
326                 returnStatus = new Status(StatusCode.BADREQUEST, "Meter record exists");
327                 return returnStatus;
328             } else if (!returnResult) {
329                 logger.error("Group record does not exist");
330                 returnStatus = new Status(StatusCode.BADREQUEST, "Meter record does not exist");
331                 return returnStatus;
332             }
333
334             for (int i = 0; i < meter.getMeterBandHeaders().getMeterBandHeader().size(); i++) {
335                 if (!meter.getFlags().isMeterBurst()) {
336                     if (0 < meter.getMeterBandHeaders().getMeterBandHeader().get(i).getBurstSize()) {
337                         logger.error("Burst size should only be associated when Burst FLAG is set");
338                         returnStatus = new Status(StatusCode.BADREQUEST,
339                                 "Burst size should only be associated when Burst FLAG is set");
340                         break;
341                     }
342                 }
343             }
344
345             if (null != returnStatus && !returnStatus.isSuccess()) {
346                 return returnStatus;
347             } else {
348                 BandType setBandType = null;
349                 DscpRemark dscpRemark = null;
350                 for (int i = 0; i < meter.getMeterBandHeaders().getMeterBandHeader().size(); i++) {
351                     setBandType = meter.getMeterBandHeaders().getMeterBandHeader().get(i).getBandType();
352                     if (setBandType instanceof DscpRemark) {
353                         dscpRemark = (DscpRemark) setBandType;
354                         if (0 > dscpRemark.getRate()) {
355
356                         }
357                     } else if (setBandType instanceof Drop) {
358                         if (0 < dscpRemark.getPercLevel()) {
359                             logger.error("Number of drop Precedence level");
360                         }
361                     } else if (setBandType instanceof Experimenter) {
362
363                     }
364                 }
365             }
366         }
367         return new Status(StatusCode.SUCCESS);
368     }
369
370     private boolean doesMeterEntryExists(MeterKey key, String meterName, String containerName) {
371         if (!originalSwMeterView.containsKey(key)) {
372             return false;
373         }
374
375         for (Entry<MeterKey, Meter> entry : originalSwMeterView.entrySet()) {
376             if (entry.getValue().getMeterName().equals(meterName)) {
377                 if (entry.getValue().getContainerName().equals(containerName)) {
378                     return true;
379                 }
380             }
381         }
382         return false;
383     }
384
385     private RpcResult<Void> commitToPlugin(internalTransaction transaction) {
386         for (Entry<InstanceIdentifier<?>, Meter> entry : transaction.additions.entrySet()) {
387
388             if (!addMeter(entry.getKey(), entry.getValue()).isSuccess()) {
389                 return Rpcs.getRpcResult(false, null, null);
390             }
391         }
392         for (@SuppressWarnings("unused")
393         Entry<InstanceIdentifier<?>, Meter> entry : transaction.updates.entrySet()) {
394
395             if (!updateMeter(entry.getKey(), entry.getValue()).isSuccess()) {
396                 return Rpcs.getRpcResult(false, null, null);
397             }
398         }
399
400         for (InstanceIdentifier<?> removal : transaction.removals) {
401             /*
402              * if (!removeMeter(entry.getKey(),entry.getValue()).isSuccess()) {
403              * return Rpcs.getRpcResult(false, null, null); }
404              */
405         }
406
407         return Rpcs.getRpcResult(true, null, null);
408     }
409
410     private final class internalTransaction implements DataCommitTransaction<InstanceIdentifier<?>, DataObject> {
411
412         private final DataModification<InstanceIdentifier<?>, DataObject> modification;
413
414         @Override
415         public DataModification<InstanceIdentifier<?>, DataObject> getModification() {
416             return modification;
417         }
418
419         public internalTransaction(DataModification<InstanceIdentifier<?>, DataObject> modification) {
420             this.modification = modification;
421         }
422
423         Map<InstanceIdentifier<?>, Meter> additions = new HashMap<>();
424         Map<InstanceIdentifier<?>, Meter> updates = new HashMap<>();
425         Set<InstanceIdentifier<?>> removals = new HashSet<>();
426
427         /**
428          * We create a plan which flows will be added, which will be updated and
429          * which will be removed based on our internal state.
430          *
431          */
432         void prepareUpdate() {
433
434             Set<Entry<InstanceIdentifier<?>, DataObject>> puts = modification.getUpdatedConfigurationData().entrySet();
435             for (Entry<InstanceIdentifier<?>, DataObject> entry : puts) {
436                 if (entry.getValue() instanceof Meter) {
437                     Meter Meter = (Meter) entry.getValue();
438                     preparePutEntry(entry.getKey(), Meter);
439                 }
440
441             }
442
443             removals = modification.getRemovedConfigurationData();
444         }
445
446         private void preparePutEntry(InstanceIdentifier<?> key, Meter meter) {
447
448             Meter original = originalSwMeterView.get(key);
449             if (original != null) {
450                 // It is update for us
451
452                 updates.put(key, meter);
453             } else {
454                 // It is addition for us
455
456                 additions.put(key, meter);
457             }
458         }
459
460         /**
461          * We are OK to go with execution of plan
462          *
463          */
464         @Override
465         public RpcResult<Void> finish() throws IllegalStateException {
466
467             RpcResult<Void> rpcStatus = commitToPlugin(this);
468             // We return true if internal transaction is successful.
469             // return Rpcs.getRpcResult(true, null, Collections.emptySet());
470             return rpcStatus;
471         }
472
473         /**
474          *
475          * We should rollback our preparation
476          *
477          */
478         @Override
479         public RpcResult<Void> rollback() throws IllegalStateException {
480             // NOOP - we did not modified any internal state during
481             // requestCommit phase
482             // return Rpcs.getRpcResult(true, null, Collections.emptySet());
483             return Rpcs.getRpcResult(true, null, null);
484
485         }
486
487     }
488
489     private final class MeterDataCommitHandler implements DataCommitHandler<InstanceIdentifier<?>, DataObject> {
490         @Override
491         public org.opendaylight.controller.md.sal.common.api.data.DataCommitHandler.DataCommitTransaction<InstanceIdentifier<?>, DataObject> requestCommit(
492                 DataModification<InstanceIdentifier<?>, DataObject> modification) {
493             // We should verify transaction
494             System.out.println("Coming in MeterDataCommitHandler");
495             internalTransaction transaction = new internalTransaction(modification);
496             transaction.prepareUpdate();
497             return transaction;
498         }
499     }
500
501     final class MeterEventListener implements SalMeterListener {
502
503         List<MeterAdded> addedMeter = new ArrayList<>();
504         List<MeterRemoved> removeMeter = new ArrayList<>();
505         List<MeterUpdated> updatedMeter = new ArrayList<>();
506
507         @Override
508         public void onMeterAdded(MeterAdded notification) {
509             // TODO Auto-generated method stub
510
511         }
512
513         @Override
514         public void onMeterRemoved(MeterRemoved notification) {
515             // TODO Auto-generated method stub
516
517         }
518
519         @Override
520         public void onMeterUpdated(MeterUpdated notification) {
521             // TODO Auto-generated method stub
522
523         }
524     }
525
526     @Override
527     public List<DataObject> get() {
528
529         List<DataObject> orderedList = new ArrayList<DataObject>();
530         Collection<Meter> meterList = originalSwMeterView.values();
531         for (Iterator<Meter> iterator = meterList.iterator(); iterator.hasNext();) {
532             orderedList.add(iterator.next());
533         }
534         return orderedList;
535     }
536
537     @Override
538     public DataObject getWithName(String name, Node n) {
539         if (this instanceof MeterConsumerImpl) {
540             Collection<Meter> meterList = originalSwMeterView.values();
541             for (Iterator<Meter> iterator = meterList.iterator(); iterator.hasNext();) {
542                 Meter meter = iterator.next();
543                 if (meter.getNode().equals(n) && meter.getMeterName().equals(name)) {
544
545                     return meter;
546                 }
547             }
548         }
549         return null;
550     }
551 }