blob: c194d50114701e74e4571980f53cf0341e9d1113 [file] [log] [blame]
Chetan Gaonkercb122cc2016-05-10 10:58:34 -07001#!/usr/bin/env python
Chetan Gaonkercfcce782016-05-10 10:10:42 -07002#
3# Copyright 2016-present Ciena Corporation
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
Chetan Gaonker7ab338c2016-04-15 17:23:17 -070017import json
18import requests
19import os,sys,time
20from nose.tools import *
21from scapy.all import *
22from OnosCtrl import OnosCtrl
Chetan Gaonker3533faa2016-04-25 17:50:14 -070023import fcntl, socket, struct
24
25def get_mac(iface = 'ovsbr0', pad = 4):
26 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
Chetan Gaonkerfb3cb5e2016-05-06 11:55:44 -070027 try:
28 info = fcntl.ioctl(s.fileno(), 0x8927, struct.pack('256s', iface[:15]))
29 except:
30 info = ['0'] * 24
Chetan Gaonker3533faa2016-04-25 17:50:14 -070031 return '0'*pad + ''.join(['%02x' %ord(char) for char in info[18:24]])
Chetan Gaonker7ab338c2016-04-15 17:23:17 -070032
33class OnosFlowCtrl:
34
35 auth = ('karaf', 'karaf')
36 controller = os.getenv('ONOS_CONTROLLER_IP') or 'localhost'
37 cfg_url = 'http://%s:8181/onos/v1/flows/' %(controller)
38
39 def __init__( self,
40 deviceId,
41 appId=0,
42 ingressPort="",
43 egressPort="",
44 ethType="",
45 ethSrc="",
46 ethDst="",
47 vlan="",
48 ipProto="",
49 ipSrc=(),
50 ipDst=(),
51 tcpSrc="",
52 tcpDst="",
53 udpDst="",
54 udpSrc="",
55 mpls=""):
56 self.deviceId = deviceId
57 self.appId = appId
58 self.ingressPort = ingressPort
59 self.egressPort = egressPort
60 self.ethType = ethType
61 self.ethSrc = ethSrc
62 self.ethDst = ethDst
63 self.vlan = vlan
64 self.ipProto = ipProto
65 self.ipSrc = ipSrc
66 self.ipDst = ipDst
67 self.tcpSrc = tcpSrc
68 self.tcpDst = tcpDst
69 self.udpDst = udpDst
70 self.udpSrc = udpSrc
71 self.mpls = mpls
72
73 @classmethod
74 def get_flows(cls, device_id):
75 return OnosCtrl.get_flows(device_id)
76
77 def addFlow(self):
78 """
79 Description:
80 Creates a single flow in the specified device
81 Required:
82 * deviceId: id of the device
83 Optional:
84 * ingressPort: port ingress device
85 * egressPort: port of egress device
86 * ethType: specify ethType
87 * ethSrc: specify ethSrc ( i.e. src mac addr )
88 * ethDst: specify ethDst ( i.e. dst mac addr )
89 * ipProto: specify ip protocol
90 * ipSrc: specify ip source address with mask eg. ip#/24
91 as a tuple (type, ip#)
92 * ipDst: specify ip destination address eg. ip#/24
93 as a tuple (type, ip#)
94 * tcpSrc: specify tcp source port
95 * tcpDst: specify tcp destination port
96 Returns:
97 True for successful requests;
98 False for failure/error on requests
99 """
100 flowJson = { "priority":100,
101 "isPermanent":"true",
102 "timeout":0,
103 "deviceId":self.deviceId,
104 "treatment":{"instructions":[]},
105 "selector": {"criteria":[]}}
106 if self.appId:
107 flowJson[ "appId" ] = self.appId
108
109 if self.egressPort:
110 flowJson[ 'treatment' ][ 'instructions' ].append( {
111 "type":"OUTPUT",
112 "port":self.egressPort } )
113 if self.ingressPort:
114 flowJson[ 'selector' ][ 'criteria' ].append( {
115 "type":"IN_PORT",
116 "port":self.ingressPort } )
117 if self.ethType:
118 flowJson[ 'selector' ][ 'criteria' ].append( {
119 "type":"ETH_TYPE",
120 "ethType":self.ethType } )
121 if self.ethSrc:
122 flowJson[ 'selector' ][ 'criteria' ].append( {
123 "type":"ETH_SRC",
124 "mac":self.ethSrc } )
125 if self.ethDst:
126 flowJson[ 'selector' ][ 'criteria' ].append( {
127 "type":"ETH_DST",
128 "mac":self.ethDst } )
129 if self.vlan:
130 flowJson[ 'selector' ][ 'criteria' ].append( {
131 "type":"VLAN_VID",
132 "vlanId":self.vlan } )
133 if self.mpls:
134 flowJson[ 'selector' ][ 'criteria' ].append( {
135 "type":"MPLS_LABEL",
136 "label":self.mpls } )
137 if self.ipSrc:
138 flowJson[ 'selector' ][ 'criteria' ].append( {
139 "type":self.ipSrc[0],
140 "ip":self.ipSrc[1] } )
141 if self.ipDst:
142 flowJson[ 'selector' ][ 'criteria' ].append( {
143 "type":self.ipDst[0],
144 "ip":self.ipDst[1] } )
145 if self.tcpSrc:
146 flowJson[ 'selector' ][ 'criteria' ].append( {
147 "type":"TCP_SRC",
148 "tcpPort": self.tcpSrc } )
149 if self.tcpDst:
150 flowJson[ 'selector' ][ 'criteria' ].append( {
151 "type":"TCP_DST",
152 "tcpPort": self.tcpDst } )
153 if self.udpSrc:
154 flowJson[ 'selector' ][ 'criteria' ].append( {
155 "type":"UDP_SRC",
156 "udpPort": self.udpSrc } )
157 if self.udpDst:
158 flowJson[ 'selector' ][ 'criteria' ].append( {
159 "type":"UDP_DST",
160 "udpPort": self.udpDst } )
161 if self.ipProto:
162 flowJson[ 'selector' ][ 'criteria' ].append( {
163 "type":"IP_PROTO",
164 "protocol": self.ipProto } )
165
166 return self.sendFlow( deviceId=self.deviceId, flowJson=flowJson)
167
168 def removeFlow(self, deviceId, flowId):
169 """
170 Description:
171 Remove specific device flow
172 Required:
173 str deviceId - id of the device
174 str flowId - id of the flow
175 Return:
176 Returns True if successfully deletes flows, otherwise False
177 """
178 # NOTE: REST url requires the intent id to be in decimal form
179 query = self.cfg_url + str( deviceId ) + '/' + str( int( flowId ) )
180 response = requests.delete(query, auth = self.auth)
181 if response:
182 if 200 <= response.status_code <= 299:
183 return True
184 else:
185 return False
186
187 return True
188
189 def findFlow(self, deviceId, **criterias):
190 flows = self.get_flows(deviceId)
191 match_keys = criterias.keys()
192 matches = len(match_keys)
193 num_matched = 0
194 for f in flows:
195 criteria = f['selector']['criteria']
196 for c in criteria:
197 if c['type'] not in match_keys:
198 continue
199 match_key, match_val = criterias.get(c['type'])
200 val = c[match_key]
201 if val == match_val:
202 num_matched += 1
203 if num_matched == matches:
204 return f['id']
205 return None
206
207 def sendFlow(self, deviceId, flowJson):
208 """
209 Description:
210 Sends a single flow to the specified device. This function exists
211 so you can bypass the addFLow driver and send your own custom flow.
212 Required:
213 * The flow in json
214 * the device id to add the flow to
215 Returns:
216 True for successful requests
217 False for error on requests;
218 """
219 url = self.cfg_url + str(deviceId)
220 response = requests.post(url, auth = self.auth, data = json.dumps(flowJson) )
221 if response.ok:
222 if response.status_code in [200, 201]:
223 log.info('Successfully POSTED flow for device %s' %str(deviceId))
224 return True
225 else:
226 log.info('Post flow for device %s failed with status %d' %(str(deviceId),
227 response.status_code))
228 return False
229 else:
230 log.error('Flow post request returned with status %d' %response.status_code)
231
232 return False