blob: b948ce5a039a40e53f75ea5f524c78de8f19c4fb [file] [log] [blame]
Jon Hallfc915882015-07-14 13:33:17 -07001#!/usr/bin/env python
2"""
3Created on 07-08-2015
4
5 TestON is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 2 of the License, or
8 ( at your option ) any later version.
9
10 TestON is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with TestON. If not, see <http://www.gnu.org/licenses/>.
17
18"""
19import json
20import os
21import requests
Jon Hallfc915882015-07-14 13:33:17 -070022
Jon Hallfc915882015-07-14 13:33:17 -070023from drivers.common.api.controllerdriver import Controller
24
25
26class OnosRestDriver( Controller ):
27
28 def __init__( self ):
29 super( Controller, self ).__init__()
30 self.ip_address = "localhost"
31 self.port = "8080"
32 self.user_name = "user"
33 self.password = "CHANGEME"
34
35 def connect( self, **connectargs ):
36 try:
37 for key in connectargs:
38 vars( self )[ key ] = connectargs[ key ]
39 self.name = self.options[ 'name' ]
40 except Exception as e:
41 main.log.exception( e )
42 try:
43 if os.getenv( str( self.ip_address ) ) != None:
44 self.ip_address = os.getenv( str( self.ip_address ) )
45 else:
46 main.log.info( self.name + ": ip set to" + self.ip_address )
47 except KeyError:
48 main.log.info( "Invalid host name," +
49 "defaulting to 'localhost' instead" )
50 self.ip_address = 'localhost'
51 except Exception as inst:
52 main.log.error( "Uncaught exception: " + str( inst ) )
53
54 self.handle = super( OnosRestDriver, self ).connect()
55 return self.handle
56
57 def send( self, ip, port, url, base="/onos/v1", method="GET",
58 query=None, data=None ):
59 """
60 Arguments:
61 str ip: ONOS IP Address
62 str port: ONOS REST Port
63 str url: ONOS REST url path.
64 NOTE that this is is only the relative path. IE "/devices"
65 str base: The base url for the given REST api. Applications could
66 potentially have their own base url
67 str method: HTTP method type
68 dict params: Dictionary to be sent in the query string for
69 the request
70 dict data: Dictionary to be sent in the body of the request
71 """
72 # TODO: Authentication - simple http (user,pass) tuple
73 # TODO: should we maybe just pass kwargs straight to response?
74 # TODO: Do we need to allow for other protocols besides http?
75 # ANSWER: Not yet, but potentially https with certificates
76 try:
77 path = "http://" + str( ip ) + ":" + str( port ) + base + url
78 main.log.info( "Sending request " + path + " using " +
79 method.upper() + " method." )
80 response = requests.request( method.upper(),
81 path,
82 params=query,
83 data=data )
84 return ( response.status_code, response.text.encode( 'utf8' ) )
85 except requests.exceptions:
86 main.log.exception( "Error sending request." )
87 return None
88 except Exception as e:
89 main.log.exception( e )
90 return None
91 # FIXME: add other exceptions
92
93 def intents( self, ip="DEFAULT", port="DEFAULT" ):
94 """
95 """
96 try:
97 output = None
98 if ip == "DEFAULT":
99 main.log.warn( "No ip given, reverting to ip from topo file" )
100 ip = self.ip_address
101 if port == "DEFAULT":
102 main.log.warn( "No port given, reverting to port from topo file" )
103 port = self.port
104 response = self.send( ip, port, url="/intents" )
105 if response:
106 main.log.debug( response )
107 # We can do some verification on the return code
108 # NOTE: Not all requests return 200 on success, usually it will be a 2XX code
109 # 3XX is a redirction, which may be given on a post to show where the new resource can be found
110 # 4XX is usually a bad request on our side
111 # 5XX will usually mean an ONOS bug
112 if response[0] == 200:
113 main.log.debug("all ok")
114 else:
115 main.log.error( "Error with REST request, response was: " +
116 str( response ) )
117 # return main.FALSE
118 output = response[1]
119
120 # NOTE: The output is slightly differen from the cli.
121 # if data = cli's json output
122 # then rest output is dict( 'intents' : data )
123
124 # We have some options here:
125 # 1) we can return the whole thing as a string as we do with cli,
126 # then change our tests to correctly parse the data
127 #
128 # 2) We could return just the data of the response, but we would
129 # have to decide if we return:
130 # 2a) the data as a dictionary or
131 # 2b) as a json string.
132 #
133 # If we return the dict, we probably want to change the
134 # cli drivers to match.
135
136 # Ideally, we would be able to switch between using the api driver
137 # and the cli driver by simply changing the .topo file. I don't
138 # know if we will achive that right away, but we should try to plan
139 # for that.
140
141
142 # 1)
143 '''
144 return output
145 '''
146
147 # 2a)
148 '''
149 import json
150 a = json.loads( output )
151 return a.get( 'intents' )
152 '''
153
154 # 2b)
155 import json
156 a = json.loads( output ).get( 'intents' )
157 b = json.dumps(a)
158 main.log.debug( b )
159 return b
160 except Exception as e:
161 main.log.exception( e )
162 return None
163
164 def intent( self, appId, intentId, ip="DEFAULT", port="DEFAULT" ):
165 """
166 Returns a single intent in dictionary form
167 """
168 try:
169 output = None
170 if ip == "DEFAULT":
171 main.log.warn( "No ip given, reverting to ip from topo file" )
172 ip = self.ip_address
173 if port == "DEFAULT":
174 main.log.warn( "No port given, reverting to port from topo file" )
175 port = self.port
176 # NOTE: REST url requires the intent id to be in decimal form
177 query = "/" + str( appId ) + "/" + str( int( intentId, 16 ) )
178 response = self.send( ip, port, url="/intents" + query )
179 if response:
180 main.log.debug( response )
181 # We can do some verification on the return code
182 # NOTE: Not all requests return 200 on success, usually it will be a 2XX code
183 # 3XX is a redirction, which may be given on a post to show where the new resource can be found
184 # 4XX is usually a bad request on our side
185 # 5XX will usually mean an ONOS bug
186 if response[0] == 200:
187 main.log.debug("all ok")
188 else:
189 main.log.error( "Error with REST request, response was: " +
190 str( response ) )
191 # return main.FALSE
192 output = response[1]
193 a = json.loads( output )
194 return a
195 except Exception as e:
196 main.log.exception( e )
197 return None