blob: 0aed3d1c760dfdbd9b6634bff67d55c601e8e143 [file] [log] [blame]
import json
import requests
import os,sys,time
from nose.tools import *
from scapy.all import *
from OnosCtrl import OnosCtrl
import fcntl, socket, struct
def get_mac(iface = 'ovsbr0', pad = 4):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
info = fcntl.ioctl(s.fileno(), 0x8927, struct.pack('256s', iface[:15]))
except:
info = ['0'] * 24
return '0'*pad + ''.join(['%02x' %ord(char) for char in info[18:24]])
class OnosFlowCtrl:
auth = ('karaf', 'karaf')
controller = os.getenv('ONOS_CONTROLLER_IP') or 'localhost'
cfg_url = 'http://%s:8181/onos/v1/flows/' %(controller)
def __init__( self,
deviceId,
appId=0,
ingressPort="",
egressPort="",
ethType="",
ethSrc="",
ethDst="",
vlan="",
ipProto="",
ipSrc=(),
ipDst=(),
tcpSrc="",
tcpDst="",
udpDst="",
udpSrc="",
mpls=""):
self.deviceId = deviceId
self.appId = appId
self.ingressPort = ingressPort
self.egressPort = egressPort
self.ethType = ethType
self.ethSrc = ethSrc
self.ethDst = ethDst
self.vlan = vlan
self.ipProto = ipProto
self.ipSrc = ipSrc
self.ipDst = ipDst
self.tcpSrc = tcpSrc
self.tcpDst = tcpDst
self.udpDst = udpDst
self.udpSrc = udpSrc
self.mpls = mpls
@classmethod
def get_flows(cls, device_id):
return OnosCtrl.get_flows(device_id)
def addFlow(self):
"""
Description:
Creates a single flow in the specified device
Required:
* deviceId: id of the device
Optional:
* ingressPort: port ingress device
* egressPort: port of egress device
* ethType: specify ethType
* ethSrc: specify ethSrc ( i.e. src mac addr )
* ethDst: specify ethDst ( i.e. dst mac addr )
* ipProto: specify ip protocol
* ipSrc: specify ip source address with mask eg. ip#/24
as a tuple (type, ip#)
* ipDst: specify ip destination address eg. ip#/24
as a tuple (type, ip#)
* tcpSrc: specify tcp source port
* tcpDst: specify tcp destination port
Returns:
True for successful requests;
False for failure/error on requests
"""
flowJson = { "priority":100,
"isPermanent":"true",
"timeout":0,
"deviceId":self.deviceId,
"treatment":{"instructions":[]},
"selector": {"criteria":[]}}
if self.appId:
flowJson[ "appId" ] = self.appId
if self.egressPort:
flowJson[ 'treatment' ][ 'instructions' ].append( {
"type":"OUTPUT",
"port":self.egressPort } )
if self.ingressPort:
flowJson[ 'selector' ][ 'criteria' ].append( {
"type":"IN_PORT",
"port":self.ingressPort } )
if self.ethType:
flowJson[ 'selector' ][ 'criteria' ].append( {
"type":"ETH_TYPE",
"ethType":self.ethType } )
if self.ethSrc:
flowJson[ 'selector' ][ 'criteria' ].append( {
"type":"ETH_SRC",
"mac":self.ethSrc } )
if self.ethDst:
flowJson[ 'selector' ][ 'criteria' ].append( {
"type":"ETH_DST",
"mac":self.ethDst } )
if self.vlan:
flowJson[ 'selector' ][ 'criteria' ].append( {
"type":"VLAN_VID",
"vlanId":self.vlan } )
if self.mpls:
flowJson[ 'selector' ][ 'criteria' ].append( {
"type":"MPLS_LABEL",
"label":self.mpls } )
if self.ipSrc:
flowJson[ 'selector' ][ 'criteria' ].append( {
"type":self.ipSrc[0],
"ip":self.ipSrc[1] } )
if self.ipDst:
flowJson[ 'selector' ][ 'criteria' ].append( {
"type":self.ipDst[0],
"ip":self.ipDst[1] } )
if self.tcpSrc:
flowJson[ 'selector' ][ 'criteria' ].append( {
"type":"TCP_SRC",
"tcpPort": self.tcpSrc } )
if self.tcpDst:
flowJson[ 'selector' ][ 'criteria' ].append( {
"type":"TCP_DST",
"tcpPort": self.tcpDst } )
if self.udpSrc:
flowJson[ 'selector' ][ 'criteria' ].append( {
"type":"UDP_SRC",
"udpPort": self.udpSrc } )
if self.udpDst:
flowJson[ 'selector' ][ 'criteria' ].append( {
"type":"UDP_DST",
"udpPort": self.udpDst } )
if self.ipProto:
flowJson[ 'selector' ][ 'criteria' ].append( {
"type":"IP_PROTO",
"protocol": self.ipProto } )
return self.sendFlow( deviceId=self.deviceId, flowJson=flowJson)
def removeFlow(self, deviceId, flowId):
"""
Description:
Remove specific device flow
Required:
str deviceId - id of the device
str flowId - id of the flow
Return:
Returns True if successfully deletes flows, otherwise False
"""
# NOTE: REST url requires the intent id to be in decimal form
query = self.cfg_url + str( deviceId ) + '/' + str( int( flowId ) )
response = requests.delete(query, auth = self.auth)
if response:
if 200 <= response.status_code <= 299:
return True
else:
return False
return True
def findFlow(self, deviceId, **criterias):
flows = self.get_flows(deviceId)
match_keys = criterias.keys()
matches = len(match_keys)
num_matched = 0
for f in flows:
criteria = f['selector']['criteria']
for c in criteria:
if c['type'] not in match_keys:
continue
match_key, match_val = criterias.get(c['type'])
val = c[match_key]
if val == match_val:
num_matched += 1
if num_matched == matches:
return f['id']
return None
def sendFlow(self, deviceId, flowJson):
"""
Description:
Sends a single flow to the specified device. This function exists
so you can bypass the addFLow driver and send your own custom flow.
Required:
* The flow in json
* the device id to add the flow to
Returns:
True for successful requests
False for error on requests;
"""
url = self.cfg_url + str(deviceId)
response = requests.post(url, auth = self.auth, data = json.dumps(flowJson) )
if response.ok:
if response.status_code in [200, 201]:
log.info('Successfully POSTED flow for device %s' %str(deviceId))
return True
else:
log.info('Post flow for device %s failed with status %d' %(str(deviceId),
response.status_code))
return False
else:
log.error('Flow post request returned with status %d' %response.status_code)
return False