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