001package com.net2plan.examples.general.onlineSim;
002/*******************************************************************************
003 * Copyright (c) 2017 Pablo Pavon Marino and others.
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the 2-clause BSD License 
006 * which accompanies this distribution, and is available at
007 * https://opensource.org/licenses/BSD-2-Clause
008 *
009 * Contributors:
010 *     Pablo Pavon Marino and others - initial API and implementation
011 *******************************************************************************/
012
013
014import cern.colt.function.tdouble.DoubleDoubleFunction;
015import cern.colt.matrix.tdouble.DoubleMatrix1D;
016import com.net2plan.interfaces.networkDesign.*;
017import com.net2plan.interfaces.simulation.IEventProcessor;
018import com.net2plan.interfaces.simulation.SimEvent;
019import com.net2plan.libraries.IPUtils;
020import com.net2plan.utils.Constants.RoutingType;
021import com.net2plan.utils.InputParameter;
022import com.net2plan.utils.Triple;
023
024import java.util.List;
025import java.util.Map;
026
027/** 
028 * Implements the reactions of an IP network governed by the OSPF/ECMP forwarding policies, for given link weigths
029 * 
030 * This algorithm implements the reactions of an IP network governed by the OSPF/ECMP forwarding policies, for given link weigths, to the following events: 
031 * <ul>
032 * <li>SimEvent.DemandAdd: Adds a new IP traffic demand, and recomputes the routing (now including the new traffic).</li>
033 * <li>SimEvent.DemandRemove: Remvoes an IP traffic demand, and recomputes the routing.</li>
034 * <li>SimEvent.DemandModify: Modifies the offered traffic of a demand, and recomputes the routing.</li>
035 * <li>SimEvent.LinkAdd: Adds a new IP link to the network, recomputes the routing tables and the routing.</li>
036 * <li>SimEvent.LinkRemove: Removes an existing IP link in the network, recomputes the routing tables and the routing.</li>
037 * <li>SimEvent.LinkModify: Modifies the capacity of an IP link. The routing is not modified, since OSPF does not react to capacity changes.</li>
038 * <li>SimEvent.NodesAndLinksChangeFailureState: Fails/repairs the indicated nodes and/or IP links, and reacts to such failures as OSPF does: the failed links are removed from the routing tables, and the network routing recomputed.</li>
039 * </ul>
040 * 
041 * This module can be used in conjunction with the {@code Online_evGen_generalGenerator} generator for simulating IP/OSPF networks. 
042 * 
043 * See the technology conventions used in Net2Plan built-in algorithms and libraries to represent IP/OSPF networks. 
044 * @net2plan.keywords IP/OSPF, Network recovery: restoration
045 * @net2plan.inputParameters 
046 * @author Pablo Pavon-Marino, Jose-Luis Izquierdo-Zaragoza
047 */
048public class Online_evProc_ipOspf extends IEventProcessor
049{
050        private InputParameter ipMaximumE2ELatencyMs = new InputParameter ("ipMaximumE2ELatencyMs", (double) -1 , "Maximum end-to-end latency of the traffic of an IP demand to consider it as lost traffic (a non-positive value means no limit)");
051        private NetworkLayer ipLayer;
052        private double stat_trafficOffered , stat_trafficCarried , stat_trafficOversubscribed , stat_trafficOutOfLatencyLimit , stat_trafficOfDemandsTraversingOversubscribedLink;
053        private double stat_transitoryInitTime , stat_timeLastChangeInNetwork;
054        
055        @Override
056        public String getDescription()
057        {
058                return "Implements the reactions of an IP network governed by the OSPF/ECMP forwarding policies, for given link weigths";
059        }
060
061        @Override
062        public List<Triple<String, String, String>> getParameters()
063        {
064                /* Returns the parameter information for all the InputParameter objects defined in this object (uses Java reflection) */
065                return InputParameter.getInformationAllInputParameterFieldsOfObject(this);
066        }
067
068        @Override
069        public void initialize(NetPlan initialNetPlan, Map<String, String> algorithmParameters, Map<String, String> simulationParameters, Map<String, String> net2planParameters)
070        {
071                /* Initialize all InputParameter objects defined in this object (this uses Java reflection) */
072                InputParameter.initializeAllInputParameterFieldsOfObject(this, algorithmParameters);
073
074                this.ipLayer = initialNetPlan.getNetworkLayer("IP"); if (ipLayer == null) throw new Net2PlanException ("IP layer not found");
075                
076                DoubleMatrix1D linkIGPWeightSetting = IPUtils.getLinkWeightVector(initialNetPlan , ipLayer);
077                linkIGPWeightSetting.assign (initialNetPlan.getVectorLinkUpState(ipLayer) , new DoubleDoubleFunction () { public double apply (double x , double y) { return y == 1? x : Double.MAX_VALUE; }  } );
078                IPUtils.setECMPForwardingRulesFromLinkWeights(initialNetPlan , linkIGPWeightSetting , ipLayer);
079                
080                finishTransitory(0);
081                stat_timeLastChangeInNetwork = 0;
082        }
083
084        @Override
085        public void processEvent(NetPlan currentNetPlan, SimEvent event)
086        {
087                /* Update with the network stage since the last event until now */
088                final double timeSinceLastChange = event.getEventTime() - stat_timeLastChangeInNetwork;
089                stat_trafficOffered += timeSinceLastChange * currentNetPlan.getVectorDemandOfferedTraffic(this.ipLayer).zSum();
090                stat_trafficCarried += timeSinceLastChange * currentNetPlan.getVectorDemandCarriedTraffic(this.ipLayer).zSum();
091                stat_trafficOversubscribed += timeSinceLastChange * currentNetPlan.getVectorLinkOversubscribedTraffic(this.ipLayer).zSum();
092                stat_trafficOfDemandsTraversingOversubscribedLink += timeSinceLastChange * currentNetPlan.getVectorDemandOfferedTraffic(this.ipLayer).zDotProduct(currentNetPlan.getVectorDemandTraversesOversubscribedLink(this.ipLayer));
093                if (ipMaximumE2ELatencyMs.getDouble () > 0) for (Demand d : currentNetPlan.getDemands (ipLayer)) if (d.getWorstCasePropagationTimeInMs() > ipMaximumE2ELatencyMs.getDouble ()) stat_trafficOutOfLatencyLimit += timeSinceLastChange * d.getOfferedTraffic();
094                
095                stat_timeLastChangeInNetwork = event.getEventTime();
096
097                if (event.getEventObject () instanceof SimEvent.DemandAdd)
098                {
099                        SimEvent.DemandAdd ev = (SimEvent.DemandAdd) event.getEventObject ();
100                        Demand d = currentNetPlan.addDemand(ev.ingressNode, ev.egressNode, ev.offeredTraffic, ev.routingType , null, ev.layer); 
101                        ev.demandAddedToFillByProcessor = d;
102                } else if (event.getEventObject () instanceof SimEvent.DemandRemove)
103                {
104                        SimEvent.DemandRemove ev = (SimEvent.DemandRemove) event.getEventObject ();
105                        ev.demand.remove (); 
106                } else if (event.getEventObject () instanceof SimEvent.DemandModify)
107                {
108                        SimEvent.DemandModify ev = (SimEvent.DemandModify) event.getEventObject ();
109                        Demand d = ev.demand; 
110//                      System.out.print ("IPOSPF DemandModify: demand " + d + ", " + (ev.modificationIsRelativeToCurrentOfferedTraffic? "RELATIVE" : "ABSOLUTE") + " , old demand offered: " + d.getOfferedTraffic() + " ");
111                        if (ev.modificationIsRelativeToCurrentOfferedTraffic) 
112                                d.setOfferedTraffic(d.getOfferedTraffic() + ev.offeredTraffic);
113                        else
114                                d.setOfferedTraffic(ev.offeredTraffic);
115                } else if (event.getEventObject () instanceof SimEvent.LinkAdd)
116                {
117                        SimEvent.LinkAdd ev = (SimEvent.LinkAdd) event.getEventObject ();
118                        Link newLink = currentNetPlan.addLink (ev.originNode , ev.destinationNode, ev.capacity , ev.lengthInKm , ev.propagationSpeedInKmPerSecond , null , ev.layer);
119                        ev.linkAddedToFillByProcessor = newLink;
120                } else if (event.getEventObject () instanceof SimEvent.LinkRemove)
121                {
122                        SimEvent.LinkRemove ev = (SimEvent.LinkRemove) event.getEventObject ();
123                        ev.link.remove();
124                } else if (event.getEventObject () instanceof SimEvent.LinkModify)
125                {
126                        SimEvent.LinkModify ev = (SimEvent.LinkModify) event.getEventObject ();
127                        ev.link.setCapacity(ev.newCapacity);
128                } else if (event.getEventObject () instanceof SimEvent.NodesAndLinksChangeFailureState)
129                {
130                        SimEvent.NodesAndLinksChangeFailureState ev = (SimEvent.NodesAndLinksChangeFailureState) event.getEventObject ();
131                        currentNetPlan.setLinksAndNodesFailureState(ev.linksToUp , ev.linksToDown , ev.nodesToUp , ev.nodesToDown);
132                }
133                else throw new Net2PlanException ("Unknown event type: " + event);
134
135                /* Link weights from netPlan, but the down links have Double.MAX_VALUE weight */
136                DoubleMatrix1D linkIGPWeightSetting = IPUtils.getLinkWeightVector(currentNetPlan , ipLayer);
137                linkIGPWeightSetting.assign (currentNetPlan.getVectorLinkUpState(ipLayer) , new DoubleDoubleFunction () { public double apply (double x , double y) { return y == 1? x : Double.MAX_VALUE; }  } );
138                IPUtils.setECMPForwardingRulesFromLinkWeights(currentNetPlan , linkIGPWeightSetting , ipLayer);
139//              System.out.println ("-- CHANGE OSPF ROUTING: linkIGPWeightSetting: "  + linkIGPWeightSetting);
140        }
141
142        @Override
143        public void finishTransitory(double simTime)
144        {
145                this.stat_trafficOffered = 0;
146                this.stat_trafficCarried = 0;
147                this.stat_trafficOversubscribed = 0;
148                this.stat_trafficOutOfLatencyLimit = 0;
149                this.stat_trafficOfDemandsTraversingOversubscribedLink = 0;
150                this.stat_transitoryInitTime = simTime;
151        }
152
153        @Override
154        public String finish(StringBuilder output, double simTime)
155        {
156                final double dataTime = simTime - stat_transitoryInitTime;
157                if (dataTime <= 0) { output.append ("<p>No time for data acquisition</p>"); return ""; }
158                output.append (String.format("<p>Time average traffic offered / carried / blocking (%f , %f , %f) </p>", stat_trafficOffered / dataTime, stat_trafficCarried / dataTime , stat_trafficOffered == 0? 0 : 1 - (stat_trafficCarried / stat_trafficOffered)));
159                output.append (String.format("<p>Time average traffic oversubscribed: %f (sum traffic oversubscription in the links) </p>", stat_trafficOversubscribed / dataTime));
160                output.append (String.format("<p>Time average traffic of demands with worse case propagation time out of latency limits: %f</p>", stat_trafficOutOfLatencyLimit / dataTime));
161                output.append (String.format("<p>Time average traffic of demands which traverse an oversubscribed link (summing all the demand offered traffic, even if only a fraction of traffic traverses oversubscribed links): %f</p>", stat_trafficOfDemandsTraversingOversubscribedLink / dataTime));
162                return "";
163        }
164}