2 Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
4 This program and the accompanying materials are made available under the
5 terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 and is available at http://www.eclipse.org/legal/epl-v10.html
8 Created on May 18, 2014
10 @author: <a href="mailto:vdemcak@cisco.com">Vaclav Demcak</a>
18 from tools.file_loader_tool import FileLoaderTools
19 from tools.xml_parser_tools import XMLtoDictParserTools
20 import xml.dom.minidom as md
23 class ColorEnum ( object ):
25 Color Enum class for coloring log output
27 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range( 8 )
29 class OF_CRUD_Test_Base( unittest.TestCase ):
32 def __init__( self, methodName = 'testCRUD', path_to_xml = None ):
35 * logger initialization (Child Class Name + xml input file nr.
36 * add a path for the xml input file to the local variable
38 super( OF_CRUD_Test_Base, self ).__init__( methodName )
39 self.path_to_xml = path_to_xml
40 self.log = logging.getLogger( '%s CRUD_test_xml_%04d - '
41 % ( self.__class__.__name__ , ( int( self.path_to_xml[6:-4] ) ) ) )
47 * load the input file to the local variable 'xml_input_stream' like a string
48 * parse the input string as DOM Object and save it in local variable 'xml_input_DOM'
50 @raise ValueError: raise ValueEror when a path is None
52 if ( self.path_to_xml is not None ) :
53 self.xml_input_stream = FileLoaderTools.load_file_to_string( self.path_to_xml )
54 self.xml_input_DOM = md.parseString( self.xml_input_stream )
56 raise ValueError( "Path to XML is None" )
60 self.log.info( "\n ------- test has ENDED -------- \n\n\n" )
63 def test_conf_PUT( self ):
65 test_conf_PUT - conf. PUT CRUD test here is only a mock implementation
66 which has to be overwritten by the subclasses
67 for a Flow, Meter, Group test suite
69 @raise NotImplementedError: always raise NotImplementedError
71 raise NotImplementedError( "Please Implement this method" )
74 def test_conf_POST( self ):
76 test_conf_POST - conf. POST create test here is only a mock implementation
77 which has to be overwritten by the subclasses
78 for a Flow, Meter, Group test suite
80 @raise NotImplementedError: always raise NotImplementedError
82 raise NotImplementedError( "Please Implement this method" )
85 def test_operations_POST( self ):
87 test_operations_POST - sal service CRUD test here is only a mock implementation
88 which has to be overwritten by the subclasses
89 for a Flow, Meter, Group test suite
91 @raise NotImplementedError: always raise NotImplementedError
93 raise NotImplementedError( "Please Implement this method" )
96 # --------- Response Helper --------------
99 def returnReverseInputTest( self, text ):
100 return ''.join( [text[len( text ) - count] for count in xrange( 1, len( text ) + 1 )] )
103 def assertDataDOM( self, orig_DOM_Doc, resp_DOM_Doc ):
105 assertDataDOM - help method for assertion the two DOM data representation
106 e.g. the request xml and the config Data Store result
107 has to be same always
108 @param orig_DOM_Doc: DOM Document sends to controller (e.g. from file input)
109 @param resp_DOM_Doc: DOM Document returns from response
110 @raise AssertionError: if response has not the expected 404 code
112 origDict = XMLtoDictParserTools.parseDOM_ToDict( orig_DOM_Doc._get_documentElement() )
113 respDict = XMLtoDictParserTools.parseDOM_ToDict( resp_DOM_Doc._get_documentElement() )
114 if ( respDict != origDict ) :
115 err_msg = '\n !!! Uploaded and stored xml, are not the same\n' \
116 ' uploaded: %s\n stored: %s\n differences: %s\n' \
117 '' % ( origDict, respDict, XMLtoDictParserTools.getDifferenceDict( origDict, respDict ) )
118 self.log.error( self._paint_msg_red( err_msg ) )
119 raise AssertionError( err_msg )
122 def _get_auth( self ):
123 return ( 'admin', 'admin' )
126 def _get_xml_result_header( self ):
127 return {'Accept': 'application/xml'}
130 def _get_xml_request_result_header( self ):
132 'Content-Type': 'application/xml',
133 'Accept': 'application/xml',
137 def put_REST_XML_conf_request( self, put_config_url, config_data ):
139 Method uses REST interface for PUT a request data to device from the input URL
140 * call PUT REST operation
141 * validate response status code to 204 or 200
142 @param url: URL to controller configuration DataStore for PUT method
143 @return: response from controller (expected code 204 or 200 OK)
144 @raise AssertionError: if response code is not 204 or 200
146 self.__log_request( put_config_url, config_data )
147 response = requests.put( put_config_url,
149 auth = self._get_auth(),
150 headers = self._get_xml_request_result_header() )
151 self.__log_response( response )
152 if ( response.status_code != 204 and response.status_code != 200 ) :
153 err_msg = '\n !!! %s Status code returned %d \n' % ( sys._getframe( 1 ).f_code.co_name, response.status_code )
154 self.log.error( self._paint_msg_red( err_msg ) )
155 raise AssertionError( err_msg )
157 self.__time_wait_conf()
161 def get_REST_XML_response( self, get_url ):
163 Method uses REST interface to GET a response from the input URL
164 * call GET REST operation
165 * validate an expectation that the data is exist
166 @param get_url: URL for GETing the node data
167 @return: response from controller (expected code 200 + data in payload)
168 @raise AssertionError: if response code is not 200
170 self.__log_request( get_url )
171 self.__time_wait_oper()
172 response = requests.get( get_url,
173 auth = self._get_auth(),
174 headers = self._get_xml_result_header() )
175 self.__log_response( response )
176 if response.status_code != 200 :
177 err_msg = '\n !!! %s Expected status code 200, but returned %d \n' % ( sys._getframe( 1 ).f_code.co_name, response.status_code )
178 self.log.error( self._paint_msg_red( err_msg ) )
179 raise AssertionError( err_msg )
181 self.__time_wait_conf()
185 def get_REST_XML_deleted_response( self, get_url ):
187 Method uses REST interface to GET the deleted data for input URL
188 * call GET REST operation
189 * validate an expectation that the data is not exist
190 @param get_url: URL - define the deleted node
191 @return: response from controller (expect 404 Not Found - No data exists.)
192 @raise AssertionError: if response has not the expected 404 code
194 self.__log_request( get_url )
195 response = requests.get( get_url,
196 auth = self._get_auth(),
197 headers = self._get_xml_result_header() )
198 self.__log_response( response )
199 if response.status_code != 404 :
200 err_msg = '\n !!! %s Expected status code 404, but returned %d \n' % ( sys._getframe( 1 ).f_code.co_name, response.status_code )
201 self.log.error( self._paint_msg_red( err_msg ) )
202 raise AssertionError( err_msg )
204 self.__time_wait_conf()
208 def delete_REST_XML_response( self, delete_url ):
210 Method uses REST DELETE operation for node data on input URL
211 * call DELETE REST operation
212 * validate response code 200 (deleted successful)
213 @param delete_url: URL - define the data node for delete process
214 @return: response from controller (expect 200 OK)
215 @raise AssertionError: if response has not the expected 200 code
217 self.__log_request( delete_url )
218 response = requests.delete( delete_url,
219 auth = self._get_auth(),
220 headers = self._get_xml_result_header() )
221 self.__log_response( response )
222 if response.status_code != 200 :
223 err_msg = '\n !!! %s Expected status code 200, but returned %d \n' % ( sys._getframe( 1 ).f_code.co_name, response.status_code )
224 self.log.error( self._paint_msg_red( err_msg ) )
225 raise AssertionError( err_msg )
227 self.__time_wait_conf()
231 def post_REST_XML_request( self, post_url, post_data ):
233 Method uses REST POST operation for node data on input URL
234 * call POST operation with input data
235 * validate response code 200 or 204
236 @param post_url: URL - define the data node or sal-service operation
237 @return: response from controller (response code expect 200 or 204)
238 @raise AssertionError: if response has not the expected 200 or 204 code
240 self.__log_request( post_url, post_data )
241 response = requests.post( post_url,
243 auth = self._get_auth(),
244 headers = self._get_xml_request_result_header() )
245 self.__log_response( response )
246 if response.status_code != 204 and response.status_code != 200 :
247 err_msg = '\n !!! %s Status code returned %d \n' % ( sys._getframe( 1 ).f_code.co_name, response.status_code )
248 self.log.error( err_msg )
249 raise AssertionError( err_msg )
251 self.__time_wait_conf()
255 def post_REST_XML_repeat_request( self, post_url, post_data ):
257 Method uses REST POST operation for add input data on node
258 specify by URL, but we expect to have the data, so service
259 has to return 409 exception code
260 * call POST operation with input data
261 * validate response code 409 Conflict (data exist)
262 @param post_url: URL - define the data node
263 @return: response from controller (expected Conflict -> Data Exist)
264 @raise AssertionError: if response has not the expected 409 Conflict code
266 self.__log_request( post_url, post_data )
267 response = requests.post( post_url,
269 auth = self._get_auth(),
270 headers = self._get_xml_request_result_header() )
271 self.__log_response( response )
272 if response.status_code != 409 :
273 err_msg = '\n !!! %s Expected status code 409, but returned %d \n' % ( sys._getframe( 1 ).f_code.co_name, response.status_code )
274 self.log.error( self._paint_msg_red( err_msg ) )
275 raise AssertionError( err_msg )
277 self.__time_wait_conf()
281 # --------- LOGGING HELP METHODS --------
282 def __log_request( self, url, data = None ):
283 self.log.info( ' Running method is "%s"' % sys._getframe( 1 ).f_code.co_name )
284 self.log.info( ' REQUEST is sending to URL : {0} '.format( self._paint_msg_blue( url ) ) )
285 if data is not None :
286 self.log.debug( ' REQUEST data : \n\n%s\n' % ( self._paint_msg_green( data ) ) )
288 self.log.debug( ' REQUEST data: %s \n' % ( self._paint_msg_green( 'None' ) ) )
291 def __log_response( self, response ):
292 self.log.info( ' Running method is "%s" ' % sys._getframe( 1 ).f_code.co_name )
293 self.log.info( ' RECEIVED status code: {0} '.format( self._paint_msg_magenta( response.status_code ) ) )
294 self.log.debug( ' RECEIVED data : \n\n%s\n' % self._paint_msg_green( response.content ) )
297 def __time_wait_oper( self ):
298 self.log.info( '......... Waiting for operational DataStore %s sec. ......... ' % self.CONTROLLER_OPERATION_DELAY )
299 time.sleep( self.CONTROLLER_OPERATION_DELAY )
302 def __time_wait_conf( self ):
303 self.log.info( '......... Waiting for controller %s sec. ......... ' % self.CONTROLLER_DELAY )
304 time.sleep( self.CONTROLLER_DELAY )
307 def _paint_msg_green( self, msg ):
308 return self.__paint_msg( msg, 2 )
311 def _paint_msg_blue ( self, msg ):
312 return self.__paint_msg( msg, ColorEnum.BLUE )
315 def _paint_msg_magenta ( self, msg ):
316 return self.__paint_msg( msg, ColorEnum.MAGENTA )
319 def _paint_msg_cyan ( self, msg ):
320 return self.__paint_msg( msg, ColorEnum.CYAN )
323 def _paint_msg_red ( self, msg ):
324 return self.__paint_msg( msg, ColorEnum.RED )
327 def _paint_msg_yellow( self, msg ):
328 return self.__paint_msg( msg, ColorEnum.YELLOW )
331 def __paint_msg ( self, msg, colorNr ):
332 if ( self.COLORING == 1 ) :
333 color = '\x1b[3%dm' % colorNr
334 return '%s %s %s' % ( color, msg, '\x1b[0m' )