package org.opendaylight.defense4all.odl;
/**
* Copyright (c) <2013> <Radware Ltd.> and others. All rights reserved.
*
* This program and the accompanying materials are made available under the terms of the Eclipse Public License
* v1.0 which accompanies this distribution, and is available at http://www.eclipse.org/legal/epl-v10.html
* @author Gera Goft
* @version 0.1
*/
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.apache.mina.filter.firewall.Subnet;
import org.opendaylight.defense4all.core.DFAppRoot;
import org.opendaylight.defense4all.core.NetNode;
import org.opendaylight.defense4all.core.NetNode.SDNNodeMode;
import org.opendaylight.defense4all.core.NetNode.Status;
import org.opendaylight.defense4all.core.OFC;
import org.opendaylight.defense4all.core.PN;
import org.opendaylight.defense4all.core.ProtectedLink;
import org.opendaylight.defense4all.core.StatsCollectionRep;
import org.opendaylight.defense4all.core.Traffic.TrafficDirection;
import org.opendaylight.defense4all.core.TrafficFloor;
import org.opendaylight.defense4all.core.TrafficPort;
import org.opendaylight.defense4all.core.TrafficPort.PortLocation;
import org.opendaylight.defense4all.core.interactionstructures.StatsCountersPlacement;
import org.opendaylight.defense4all.core.TrafficTuple;
import org.opendaylight.defense4all.framework.core.ExceptionControlApp;
import org.opendaylight.defense4all.framework.core.FrameworkMain.ResetLevel;
import org.opendaylight.defense4all.framework.core.HealthTracker;
import org.opendaylight.defense4all.odl.pojos.FlowConfigNoProtocol.ActionType;
import org.opendaylight.defense4all.odl.pojos.FlowStat;
import org.opendaylight.defense4all.odl.pojos.FlowStatistics;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class OdlStatsCollectionRep extends StatsCollectionRep {
public Odl odl = null;
public int odlStatsCollectionInterval;
public int defaultOdlStatsCollectionInterval;
Logger log = LoggerFactory.getLogger(this.getClass());
/* Constructor for Spring */
public OdlStatsCollectionRep() {
super();
}
/* Setters for Spring */
public void setOdl(Odl odl) {this.odl = odl;}
public void setDefaultOdlStatsCollectionInterval ( int defaultOdlStatsCollectionInterval) {
this.defaultOdlStatsCollectionInterval = defaultOdlStatsCollectionInterval;
}
/** Post-constructor initialization */
@Override
public void init() throws ExceptionControlApp {
super.init();
odl.init();
}
/** Pre-shutdown cleanup */
@Override
public void finit() {
super.finit();
odl.finit();
}
/** Reset
* @throws ExceptionControlApp */
@Override
public void reset(ResetLevel resetLevel) throws ExceptionControlApp {
super.reset(resetLevel);
odl.reset(resetLevel);
}
/**
* Offer all possible placements of stats counters that monitor the traffic to protected network denoted
* by the passed in pnKey param.
*
* @param param_name param description
* @return return An array of all possible placements of stats counters that monitor the subject traffic.
* Each placement contains a set of stats counter locations in the OF network and a common QoS specification
* in such a placement. QoS degrading factors can be aggregating counters of multiple traffics into a single
* one monitoring only the sum of all traffics, time sharing counter placement, Qos degradation to other
* traffics monitoring (if location is preempted by these counters).
* @throws ExceptionControlApp
* @throws exception_type circumstances description
*/
@Override
public List<StatsCountersPlacement> offerCounterPlacements(String pnKey) throws ExceptionControlApp {
ArrayList<String> statsCounterLocs = new ArrayList<String>();
PN pn = null;
try {
Hashtable<String,Object> pnRow = dfAppRoot.pNsRepo.getRow(pnKey);
pn = new PN(pnRow);
} catch (ExceptionControlApp e) {
log.error("Excepted trying to inflate PN " + pnKey, e);
throw e;
}
for(String netNodeLabel : pn.netNodeLabels) {
Hashtable<String, Object> netNodeRow;
try {
netNodeRow = dfAppRoot.netNodesRepo.getRow(netNodeLabel);
} catch (ExceptionControlApp e) {
log.error("Failed to retrieve netNode row for " + netNodeLabel);
continue;
}
if(netNodeRow == null) continue; // NetNode has not been defined to the application
Status netNodeStatus = Status.valueOf((String) netNodeRow.get(NetNode.STATUS));
if(netNodeStatus != NetNode.Status.ACTIVE) continue; // NetNode is not ready yet, or is being removed
statsCounterLocs.add(netNodeLabel);
}
StatsCountersPlacement statsCountersPlacement = new StatsCountersPlacement(statsCounterLocs, null);
ArrayList<StatsCountersPlacement> statsCounterPlacements = new ArrayList<StatsCountersPlacement>();
statsCounterPlacements.add(statsCountersPlacement);
StringBuilder sb = new StringBuilder();
sb.append("OdlStatsCollectionRep offers to place stats counters for "); sb.append(pnKey); sb.append(" at - ");
for(StatsCountersPlacement placement : statsCounterPlacements) {
sb.append(placement.toString()); sb.append(", ");
}
sb.setLength(sb.length() - 2); // Remove the last ", "
fMain.getFR().logRecord(DFAppRoot.FR_DF_OPERATIONAL, sb.toString());
return statsCounterPlacements;
}
/**
* Initialize connectivity to the specified OFC.
* @throws ExceptionControlApp
*/
@Override
protected void initConnectionToOFC(String ofcKey) throws ExceptionControlApp {
odl.initConnectionToOFC(ofcKey);
odl.retrieveTopology(ofcKey);
notifyTopologyChanged();
}
/**
* ####
* @param param_name param description
* @return return description
* @throws ExceptionControlApp
* @throws exception_type circumstances description
*/
@Override
public void addNetNode(String netNodeKey) throws ExceptionControlApp {
odl.addNetNode(netNodeKey);
notifyTopologyChanged();
}
/**
* ####
* @param param_name param description
* @return return description
* @throws ExceptionControlApp
* @throws exception_type circumstances description
*/
@Override
public void removeNetNode(String netNodeKey) throws ExceptionControlApp {
odl.removeNetNode(netNodeKey);
notifyTopologyChanged();
}
/**
* Set counter in OFSs.
* @param param_name param description
* @return return description
* @throws Exception
* @throws exception_type circumstances description
*/
@Override
public String addPeacetimeCounterTrafficFloor(String pnKey, String location) throws ExceptionControlApp {
TrafficFloor trafficFloor; NetNode netNode; OdlFlowConfigInfo configInfoCommon;
OdlFlowConfigInfo configInfoClone; boolean success = true; String trafficFloorKey;
Hashtable<String,Object> netNodeRow;
try {
netNodeRow = dfAppRoot.netNodesRepo.getRow(location);
if(netNodeRow == null || NetNode.isRemoved(location)) {
log.warn("Location (NetNode) " + location + " specified for PN " + pnKey + " is not known to Defense4All");
return null;
}
} catch (ExceptionControlApp e1) {
String msg = "Excepted most likely trying to retrieve elements from repos for " + pnKey + " " + location;
log.error(msg, e1);
fMain.getFR().logRecord(DFAppRoot.FR_DF_FAILURE, "Failed to add peacetime counter traffic floor for " + pnKey
+ " at " + location);
throw new ExceptionControlApp(msg, e1);
}
fMain.getFR().logRecord(DFAppRoot.FR_DF_OPERATIONAL, "Adding peacetime counter traffic floor for " + pnKey
+ " at " + location);
try {
/* Create the trafficFloor object */
trafficFloor = new TrafficFloor();
trafficFloor.pnKey = pnKey; trafficFloor.nodeLabel = location;
trafficFloor.nodeId = (String) netNodeRow.get(NetNode.ID);
trafficFloor.floorBase = TrafficFloor.FLOOR_PEACETIME_START;
trafficFloorKey = trafficFloor.generateAndSetKey();
/* Check if we already added this counter. Can be if the application is restarted and PN is reintroduced.
* In such a case the counter is already installed in the location. */
Hashtable<String,Object> trafficFloorRow;
trafficFloorRow = dfAppRoot.trafficFloorsRepo.getRow(trafficFloorKey);
if(trafficFloorRow != null) return trafficFloorKey;
netNode = new NetNode(netNodeRow);
configInfoCommon = new OdlFlowConfigInfo();
configInfoCommon.nodeLabel = location; configInfoCommon.etherType = 2048;
String dstAddrStr = (String) dfAppRoot.pNsRepo.getCellValue(pnKey, PN.DST_ADDR);
int dstAddrLen = (Integer) dfAppRoot.pNsRepo.getCellValue(pnKey, PN.DST_ADDR_PREFIX_LEN);
Subnet pnAddrSubnet = new Subnet(InetAddress.getByName(dstAddrStr), dstAddrLen);
configInfoCommon.nwDst = pnAddrSubnet.toString();
configInfoCommon.actions = new ArrayList<String>();
configInfoCommon.forRates = true; configInfoCommon.forTrafficLearning = true; // Stats collection flow entry
configInfoCommon.direction = TrafficDirection.INBOUND;
configInfoCommon.trafficFloorKey = trafficFloorKey;
} catch (Throwable e1) {
String msg = "Excepted most likely trying to retrieve elements from repos for " + pnKey + " " + location;
log.error(msg, e1);
fMain.getFR().logRecord(DFAppRoot.FR_DF_FAILURE, "Failed to add peacetime counter traffic floor for " + pnKey
+ " at " + location);
throw new ExceptionControlApp(msg, e1);
}
if(netNode.sdnNodeMode == SDNNodeMode.sdnenabledhybrid) { // Loop over all netnode traffic ports
configInfoCommon.actions.add(ActionType.HW_PATH.name()); // "Send to normal" (non-OF routing)
if(netNode.trafficPorts == null) {
String msg = "Got null trafficPorts in netNode " + location;
log.error(msg);
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
throw new ExceptionControlApp(msg);
}
Iterator<Map.Entry<String,TrafficPort>> iter = netNode.trafficPorts.entrySet().iterator();
TrafficPort trafficPort;
while(iter.hasNext()) {
trafficPort = iter.next().getValue();
if(trafficPort.location!= PortLocation.north) continue;
configInfoClone = new OdlFlowConfigInfo(configInfoCommon);
configInfoClone.ingressPort = trafficPort.number;
fMain.getFR().logRecord(DFAppRoot.FR_OFC_OPERATIONAL, "Setting four flow entries for " + pnKey
+ " at SDN hybrid NetNode " + location + ", traffic port " + trafficPort.label);
success &= setFourFlowEntries(configInfoClone, trafficFloor, trafficFloor.floorBase);
String msg = "setting flow entries for "+pnKey+" at NetNode "+location+", traffic port "+trafficPort.label;
if(!success)
fMain.getFR().logRecord(DFAppRoot.FR_OFC_FAILURE, "Failed " + msg);
}
} else { // SDNNodeMode.sdnenablednative - Loop over all netnode protected links
Iterator<Map.Entry<String,ProtectedLink>> iter = netNode.protectedLinks.entrySet().iterator();
ProtectedLink protectedLink; String action;
while(iter.hasNext()) {
protectedLink = iter.next().getValue();
configInfoClone = new OdlFlowConfigInfo(configInfoCommon);
configInfoClone.id = odl.getUniqueCookie();
configInfoClone.ingressPort = protectedLink.northPort;
action = ActionType.OUTPUT.name() + "=" + protectedLink.southPort; // Send to matching out port
configInfoClone.actions.add(action);
fMain.getFR().logRecord(DFAppRoot.FR_OFC_OPERATIONAL, "Setting four flow entries for " + pnKey
+ " at SDN native NetNode " + location + ", north port of protected link " + protectedLink.label);
success &= setFourFlowEntries(configInfoClone, trafficFloor, trafficFloor.floorBase);
String msg="setting flow entries for "+pnKey+" at NetNode "+location+", north port of "+protectedLink.label;
if(!success)
fMain.getFR().logRecord(DFAppRoot.FR_OFC_FAILURE, "Failed " + msg);
}
}
dfAppRoot.trafficFloorsRepo.setRow(trafficFloorKey, trafficFloor.toRow());
if(!success) {return null;}
return trafficFloorKey;
}
/* Generate key, install the 4 flow entries in the netnode, record them in repo and in their traffic floor */
protected boolean setFourFlowEntries(OdlFlowConfigInfo configInfo, TrafficFloor trafficFloor, short floor) {
boolean success; int tcpId; String tcpKey; int udpId; String udpKey; int icmpId; String icmpKey;
success = setFlowEntry(configInfo, trafficFloor, (short) 6, (short) (floor + TrafficFloor.FLOOR_TCP_PRIORITY));
if(!success) {
return false;
}
tcpId = configInfo.id; tcpKey = configInfo.key;
success &= setFlowEntry(configInfo, trafficFloor, (short) 17,(short) (floor + TrafficFloor.FLOOR_UDP_PRIORITY));
if(!success) {
try {
odl.deleteOpenFlowEntry(Integer.toString(tcpId), tcpKey);
} catch (ExceptionControlApp e) {
String msg = "Failed to delete flow entries during rollback for " + configInfo.nodeLabel + " " + floor;
log.error(msg, e);
fMain.getFR().logRecord(DFAppRoot.FR_DF_FAILURE, msg);
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
}
return false;
}
udpId = configInfo.id; udpKey = configInfo.key;
success &= setFlowEntry(configInfo, trafficFloor, (short) 1, (short) (floor + TrafficFloor.FLOOR_ICMP_PRIORITY));
if(!success) {
try {
odl.deleteOpenFlowEntry(Integer.toString(tcpId), tcpKey);
odl.deleteOpenFlowEntry(Integer.toString(udpId), udpKey);
} catch (ExceptionControlApp e) {
String msg = "Failed to delete flow entries during rollback for " + configInfo.nodeLabel + " " + floor;
log.error(msg, e);
fMain.getFR().logRecord(DFAppRoot.FR_DF_FAILURE, msg);
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
}
return false;
}
icmpId = configInfo.id; icmpKey = configInfo.key;
success &= setFlowEntry(configInfo, trafficFloor, (short) 0, (short) (floor + TrafficFloor.FLOOR_OTHER_PRIORITY));
if(!success) {
try {
odl.deleteOpenFlowEntry(Integer.toString(tcpId), tcpKey);
odl.deleteOpenFlowEntry(Integer.toString(udpId), udpKey);
odl.deleteOpenFlowEntry(Integer.toString(icmpId), icmpKey);
} catch (ExceptionControlApp e) {
String msg = "Failed to delete flow entries during rollback for " + configInfo.nodeLabel + " " + floor;
log.error(msg, e);
fMain.getFR().logRecord(DFAppRoot.FR_DF_FAILURE, msg);
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
}
return false;
}
return true;
}
/* set protocol and key, install the flow entry in the netnode, record in repo and in its traffic floor */
protected boolean setFlowEntry(OdlFlowConfigInfo configInfo, TrafficFloor trafficFloor, short protocol, short priority) {
configInfo.protocol = protocol; configInfo.floor = priority;
configInfo.id = odl.getUniqueCookie();
configInfo.generateAndSetKey();
boolean success = odl.setFlowEntry(configInfo, trafficFloor);
return success;
}
/**
* Remove counter from OFSs.
* @param param_name param description
* @return return description
* @throws ExceptionControlApp
* @throws exception_type circumstances description
*/
@Override
public void removeTrafficFloor(String trafficFloorKey) throws ExceptionControlApp {
odl.removeTrafficFloor(trafficFloorKey);
}
/**
* #### method description ####
* @param param_name param description
* @return return description
* @throws ExceptionControlApp
* @throws Exception
* @throws exception_type circumstances description
*/
@Override
public TrafficTuple getStats(String trafficFloorKey) throws ExceptionControlApp {
FlowStatistics flowStatistics = null;
Hashtable<String, Object> trafficFloorRow; TrafficFloor trafficFloor;
try {
trafficFloorRow = dfAppRoot.trafficFloorsRepo.getRow(trafficFloorKey);
trafficFloor = new TrafficFloor(trafficFloorRow);
} catch (ExceptionControlApp e1) {
String msg = "Excepted trying to retrieve from repo traffic floor for " + trafficFloorKey;
log.error(msg, e1);
fMain.getHealthTracker().reportHealthIssue(HealthTracker.MINOR_HEALTH_ISSUE);
throw new ExceptionControlApp(msg, e1);
}
try {
flowStatistics = odl.flowEntryMgr.getOpenFlowStats(trafficFloor.nodeId);
if(flowStatistics == null) return null;
} catch (Exception e) { return null;}
String flowEntryId; OdlFlowConfigInfo fi; int port;
TrafficTuple stats = new TrafficTuple();
for(FlowStat flowStat: flowStatistics.flowStatistic) {
try {
flowEntryId = String.valueOf(flowStat.flow.id);
if(!trafficFloor.flowConfigInfoKeys.containsKey(flowEntryId)) continue; // flowEntry not of this floor
fi = odl.odlFlowConfigs.get(String.valueOf(flowEntryId));
if(fi == null) return null; // Internal inconsistency
if(!fi.forRates) continue; // The flow entry is not for stats. (Can be for diversion).
try {
port = Integer.valueOf(fi.tpDst);
} catch (Exception e) {port = 0;}
stats.setTrafficData(fi.protocol, port, flowStat.byteCount, flowStat.packetCount, fi.forTrafficLearning,
fi.direction);
} catch (Throwable e1) {
log.error("Excepted in loop for flowStat " + flowStat.flow); // Ignore and continue
}
}
return stats;
}
@Override
public String getTrafficFloorLocation(String trafficFloorKey) throws ExceptionControlApp {
String node = (String) dfAppRoot.trafficFloorsRepo.getCellValue(trafficFloorKey, TrafficFloor.NODE_LABEL);
if(node == null) {
log.error("Received null node from trafficFllorsRepo for key " + trafficFloorKey);
throw new ExceptionControlApp("Received null node from trafficFllorsRepo for key " + trafficFloorKey);
}
return node;
}
@Override
protected void actionSwitcher(int actionCode, Object param) {
super.actionSwitcher(actionCode, param);
}
public void test(Properties props) {
odl.test(props);
}
@Override
public int getStatsCollectionInterval() {
return odlStatsCollectionInterval;
}
@Override
public void setStatsCollectionInterval(String ofcKey) throws ExceptionControlApp {
int ofcStatsIntervalInSecs = 0;
try {
Object obj = dfAppRoot.oFCsRepo.getCellValue(ofcKey, OFC.STATS_INTERVAL);
if(obj != null) ofcStatsIntervalInSecs = (Integer) obj;
if ( ofcStatsIntervalInSecs == 0 ) {
log.info("Setting StatsCollectionInterval to default value "+defaultOdlStatsCollectionInterval);
odlStatsCollectionInterval = defaultOdlStatsCollectionInterval;
}
odlStatsCollectionInterval = ofcStatsIntervalInSecs;
dfAppRoot.oFCsRepo.setCell(ofcKey, OFC.STATS_INTERVAL,odlStatsCollectionInterval);
} catch ( Throwable e) {
throw new ExceptionControlApp("Fail to set StatCollectionInterval", e);
}
}
}