Adding ONOS Segment Routing CLI files to new repo
diff --git a/sdncon/stats/tests.py b/sdncon/stats/tests.py
new file mode 100755
index 0000000..f2c69e1
--- /dev/null
+++ b/sdncon/stats/tests.py
@@ -0,0 +1,394 @@
+#
+# Copyright (c) 2013 Big Switch Networks, Inc.
+#
+# Licensed under the Eclipse Public License, Version 1.0 (the
+# "License"); you may not use this file except in compliance with the
+# License. You may obtain a copy of the License at
+#
+# http://www.eclipse.org/legal/epl-v10.html
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+# implied. See the License for the specific language governing
+# permissions and limitations under the License.
+#
+
+from django.test import TestCase, Client
+import urllib
+from django.utils import simplejson
+from .data import set_use_test_keyspace, flush_stats_db
+import time
+from sdncon.controller.config import get_local_controller_id
+
+def setUpModule():
+ set_use_test_keyspace()
+
+def test_construct_rest_url(path, query_params=None):
+ url = '/rest/%s' % path
+ if query_params:
+ url += '?'
+ url += urllib.urlencode(query_params)
+ return url
+
+def test_get_rest_data(path, query_params=None):
+ url = test_construct_rest_url(path, query_params)
+ c = Client()
+ response = c.get(url)
+ return response
+
+def test_put_rest_data(obj, path, query_params=None):
+ url = test_construct_rest_url(path, query_params)
+ data = simplejson.dumps(obj)
+ c = Client()
+ response = c.put(url , data, 'application/json')
+ return response
+
+def test_delete_rest_data(path, query_params=None):
+ url = test_construct_rest_url(path, query_params)
+ c = Client()
+ response = c.delete(url)
+ return response
+
+BASE_TIME = 1297278987000
+SECONDS_CONVERT = 1000
+MINUTES_CONVERT = 60 * SECONDS_CONVERT
+HOURS_CONVERT = 60 * MINUTES_CONVERT
+DAYS_CONVERT = 24 * HOURS_CONVERT
+
+def make_timestamp(day, hour=0, minute=0, second=0, milliseconds=0):
+ timestamp = BASE_TIME + (day * DAYS_CONVERT) + (hour * HOURS_CONVERT) + \
+ (minute * MINUTES_CONVERT) + (second * SECONDS_CONVERT) + milliseconds
+ return timestamp
+
+class StatsTestCase(TestCase):
+ def tearDown(self):
+ flush_stats_db()
+
+class BasicStatsTest(StatsTestCase):
+
+ STATS_DATA = {
+ 'controller-stats': {
+ '192.168.1.1': {
+ 'cpu-system': [
+ {'timestamp':make_timestamp(1,0),'value':1},
+ {'timestamp':make_timestamp(1,1),'value':2},
+ {'timestamp':make_timestamp(1,2),'value':3},
+ {'timestamp':make_timestamp(1,3),'value':4},
+ {'timestamp':make_timestamp(1,4),'value':5},
+ {'timestamp':make_timestamp(2,1),'value':6},
+ {'timestamp':make_timestamp(2,2),'value':7},
+ {'timestamp':make_timestamp(3,5),'value':8},
+ {'timestamp':make_timestamp(3,8),'value':9},
+ {'timestamp':make_timestamp(4,10),'value':10},
+ {'timestamp':make_timestamp(4,11),'value':11},
+ {'timestamp':make_timestamp(10,11),'value':12},
+ ],
+ 'cpu-idle': [
+ {'timestamp':make_timestamp(1,1),'value':80},
+ {'timestamp':make_timestamp(1,2),'value':83},
+ {'timestamp':make_timestamp(1,3),'value':82},
+ {'timestamp':make_timestamp(1,4),'value':79},
+ {'timestamp':make_timestamp(1,5),'value':85},
+ ]
+ }
+ },
+ 'switch-stats': {
+ '00:01:02:03:04:05': {
+ 'flow-count': [
+ {'timestamp':make_timestamp(1,0),'value':60},
+ {'timestamp':make_timestamp(1,1),'value':88},
+ {'timestamp':make_timestamp(1,2),'value':102},
+ ],
+ 'packet-count': [
+ {'timestamp':make_timestamp(1,0),'value':100},
+ {'timestamp':make_timestamp(1,1),'value':120},
+ {'timestamp':make_timestamp(1,2),'value':160},
+ ],
+ 'packet-count__arp': [
+ {'timestamp':make_timestamp(1,0),'value':20},
+ {'timestamp':make_timestamp(1,3),'value':25},
+ ],
+ 'packet-count__lldp': [
+ {'timestamp':make_timestamp(1,0),'value':30},
+ {'timestamp':make_timestamp(1,4),'value':15},
+ ]
+ }
+ }
+ }
+
+ def check_stats_results(self, returned_results, expected_results, message=None):
+ self.assertEqual(len(returned_results), len(expected_results), message)
+ for i in range(len(returned_results)):
+ expected_timestamp = expected_results[i]['timestamp']
+ returned_timestamp = returned_results[i][0]
+ expected_value = expected_results[i]['value']
+ returned_value = returned_results[i][1]
+ self.assertEqual(expected_timestamp, returned_timestamp, message)
+ self.assertEqual(expected_value, returned_value, message)
+
+ def setUp(self):
+ response = test_put_rest_data(self.STATS_DATA, 'v1/stats/data/local')
+ self.assertEqual(response.status_code, 200)
+
+ def test_get_stats(self):
+ # Get all of the cpu-system stat data
+ response = test_get_rest_data('v1/stats/data/local/controller/192.168.1.1/cpu-system', {'start-time':make_timestamp(1,0),'end-time':make_timestamp(10,11), 'sample-interval':0})
+ self.assertEqual(response.status_code, 200)
+ results = simplejson.loads(response.content)
+ self.check_stats_results(results, self.STATS_DATA['controller-stats']['192.168.1.1']['cpu-system'])
+
+ # Get just one days data of the cpu-system stat
+ response = test_get_rest_data('v1/stats/data/local/controller/192.168.1.1/cpu-system', {'start-time':make_timestamp(1,0),'end-time':make_timestamp(1,4), 'sample-interval':0})
+ self.assertEqual(response.status_code, 200)
+ results = simplejson.loads(response.content)
+ self.check_stats_results(results, self.STATS_DATA['controller-stats']['192.168.1.1']['cpu-system'][:5])
+
+ # Get two day range for cpu-system
+ response = test_get_rest_data('v1/stats/data/local/controller/192.168.1.1/cpu-system', {'start-time':make_timestamp(1,2,10),'end-time':make_timestamp(2,2,20), 'sample-interval':0})
+ self.assertEqual(response.status_code, 200)
+ results = simplejson.loads(response.content)
+ self.check_stats_results(results, self.STATS_DATA['controller-stats']['192.168.1.1']['cpu-system'][3:7])
+
+ # Get all of the flow-count switch stat data
+ response = test_get_rest_data('v1/stats/data/local/switch/00:01:02:03:04:05/flow-count', {'start-time':make_timestamp(1,0),'end-time':make_timestamp(2,0), 'sample-interval':0})
+ self.assertEqual(response.status_code, 200)
+ results = simplejson.loads(response.content)
+ self.check_stats_results(results, self.STATS_DATA['switch-stats']['00:01:02:03:04:05']['flow-count'])
+
+ # Get part of the packet-count switch stat data
+ response = test_get_rest_data('v1/stats/data/local/switch/00:01:02:03:04:05/packet-count', {'start-time':make_timestamp(1,0),'end-time':make_timestamp(1,1), 'sample-interval':0})
+ self.assertEqual(response.status_code, 200)
+ results = simplejson.loads(response.content)
+ self.check_stats_results(results, self.STATS_DATA['switch-stats']['00:01:02:03:04:05']['packet-count'][:2])
+
+ def test_delete_stats(self):
+ # Delete all but the first 2 and last 2 sample points
+ response = test_delete_rest_data('v1/stats/data/local/controller/192.168.1.1/cpu-system', {
+ 'start-time': self.STATS_DATA['controller-stats']['192.168.1.1']['cpu-system'][2]['timestamp'],
+ 'end-time':self.STATS_DATA['controller-stats']['192.168.1.1']['cpu-system'][-3]['timestamp']})
+ self.assertEquals(response.status_code, 200)
+
+ response = test_get_rest_data('v1/stats/data/local/controller/192.168.1.1/cpu-system', {'start-time':make_timestamp(1,0),'end-time':make_timestamp(10,11), 'sample-interval':0})
+ self.assertEqual(response.status_code, 200)
+ results = simplejson.loads(response.content)
+ self.check_stats_results(results, self.STATS_DATA['controller-stats']['192.168.1.1']['cpu-system'][:2] + self.STATS_DATA['controller-stats']['192.168.1.1']['cpu-system'][-2:])
+
+ def test_stats_target_types(self):
+
+ local_controller_id = get_local_controller_id()
+
+ # Check getting the list of all target types
+ response = test_get_rest_data('v1/stats/target/local/')
+ self.assertEqual(response.status_code, 200)
+ results = simplejson.loads(response.content)
+ self.assertEqual(len(results.keys()), 2)
+ self.assertTrue('controller' in results)
+ self.assertTrue('switch' in results)
+
+ # Check getting the info for the controller target type
+ response = test_get_rest_data('v1/stats/target/local/controller')
+ self.assertEqual(response.status_code, 200)
+ results = simplejson.loads(response.content)
+ self.assertEqual(len(results.keys()), 1)
+ self.assertTrue('192.168.1.1' in results)
+ controller_info = results['192.168.1.1']
+ #self.assertEqual(controller_info['controller'], local_controller_id)
+ self.assertEqual(controller_info['last-updated'], make_timestamp(10,11))
+
+ # Check getting the info for the switch target type
+ response = test_get_rest_data('v1/stats/target/local/switch')
+ self.assertEqual(response.status_code, 200)
+ results = simplejson.loads(response.content)
+ self.assertEqual(len(results.keys()), 1)
+ self.assertTrue('00:01:02:03:04:05' in results)
+ switch_info = results['00:01:02:03:04:05']
+ self.assertEqual(switch_info['controller'], local_controller_id)
+ self.assertEqual(switch_info['last-updated'], make_timestamp(1,4))
+
+ def check_stats_type_attributes(self, attributes, expected_last_updated,
+ expected_target_type):
+ last_updated = attributes.get('last-updated')
+ self.assertEqual(last_updated, expected_last_updated)
+ target_type = attributes.get('target-type')
+ self.assertEqual(target_type, expected_target_type)
+
+ def test_stats_type_index(self):
+ response = test_get_rest_data('v1/stats/index/local/controller/192.168.1.1')
+ self.assertEqual(response.status_code, 200)
+ results = simplejson.loads(response.content)
+ self.assertEqual(len(results), 2)
+ attributes = results['cpu-system']
+ self.assertEqual(len(attributes), 1)
+ self.assertEqual(attributes['last-updated'], make_timestamp(10,11))
+ attributes = results['cpu-idle']
+ self.assertEqual(len(attributes), 1)
+ self.assertEqual(attributes['last-updated'], make_timestamp(1,5))
+
+ response = test_get_rest_data('v1/stats/index/local/switch/00:01:02:03:04:05')
+ self.assertEqual(response.status_code, 200)
+ results = simplejson.loads(response.content)
+ self.assertEqual(len(results), 2)
+ attributes = results['flow-count']
+ self.assertEqual(len(attributes), 1)
+ self.assertEqual(attributes['last-updated'], make_timestamp(1,2))
+ attributes = results['packet-count']
+ self.assertEqual(len(attributes), 2)
+ self.assertEqual(attributes['last-updated'], make_timestamp(1,2))
+ parameters = attributes['parameters']
+ self.assertEqual(len(parameters), 2)
+ attributes = parameters['arp']
+ self.assertEqual(len(attributes), 1)
+ self.assertEqual(attributes['last-updated'], make_timestamp(1,3))
+ attributes = parameters['lldp']
+ self.assertEqual(len(attributes), 1)
+ self.assertEqual(attributes['last-updated'], make_timestamp(1,4))
+
+ response = test_get_rest_data('v1/stats/index/local/controller/192.168.1.1/cpu-system')
+ self.assertEqual(response.status_code, 200)
+ attributes = simplejson.loads(response.content)
+ self.assertEqual(len(attributes), 1)
+ self.assertEqual(attributes['last-updated'], make_timestamp(10,11))
+
+class StatsMetadataTest(StatsTestCase):
+
+ def check_metadata(self, stats_type, stats_metadata):
+ # The name in the metadata should match the stats_type
+ name = stats_metadata.get('name')
+ self.assertEqual(stats_type, name)
+
+ # The target_type is a required value, so it should be present in the metadata
+ target_type = stats_metadata.get('target_type')
+ self.assertNotEqual(target_type, None)
+
+ # NOTE: The following assertion works for now, since we only support these
+ # two target types. Eventually we may support other target types (in which
+ # case we'd need to add the new ones to the tuple) or else custom target
+ # types can be added (in which case we'd maybe want to remove this assertion.
+ self.assertTrue(target_type in ('controller','switch'))
+
+ # verbose_name is set automatically by the REST code if it isn't set
+ # explicitly in the metadata, so it should always be present.
+ verbose_name = stats_metadata.get('verbose_name')
+ self.assertNotEqual(verbose_name, None)
+
+ def test_stats_metadata(self):
+ response = test_get_rest_data('v1/stats/metadata/default')
+ self.assertEqual(response.status_code, 200)
+ stats_metadata_dict = simplejson.loads(response.content)
+ self.assertEqual(type(stats_metadata_dict), dict)
+ for stats_type, stats_metadata in stats_metadata_dict.items():
+ # Check that the metadata looks reasonable
+ self.check_metadata(stats_type, stats_metadata)
+
+ # Fetch the metadata for the individual stats type and check that it matches
+ response2 = test_get_rest_data('v1/stats/metadata/default/' + stats_type)
+ self.assertEqual(response2.status_code, 200)
+ stats_metadata2 = simplejson.loads(response2.content)
+ self.assertEqual(stats_metadata, stats_metadata2)
+
+ def test_invalid_stats_type(self):
+ # Try getting a stats type that doesn't exist
+ response = test_get_rest_data('v1/stats/metadata/default/foobar')
+ self.assertEqual(response.status_code, 404)
+ error_result = simplejson.loads(response.content)
+ self.assertEqual(error_result.get('error_type'), 'RestResourceNotFoundException')
+
+class LatestStatTest(StatsTestCase):
+
+ def do_latest_stat(self, target_type, target_id):
+ current_timestamp = int(time.time() * 1000)
+ for i in range(23,-1,-1):
+ # Try with different offsets. Sort of arbitrary list here. Potentially add
+ # new offsets to test specific edge cases
+ offset_list = [0, 3599999, 100, 30000, 400000, 3000000]
+ timestamp = current_timestamp - (i * 3600000) - offset_list[(i+1)%len(offset_list)]
+ stats_data = {target_type + '-stats': {target_id: {'test-stat': [{'timestamp': timestamp, 'value': i}]}}}
+ response = test_put_rest_data(stats_data, 'v1/stats/data/local')
+ self.assertEqual(response.status_code, 200)
+ response = test_get_rest_data('v1/stats/data/local/%s/%s/test-stat' % (target_type, target_id))
+ self.assertEqual(response.status_code, 200)
+ results = simplejson.loads(response.content)
+ self.assertEqual(timestamp, results[0])
+ self.assertEqual(i, results[1])
+
+ def test_controller_latest_stat(self):
+ self.do_latest_stat('controller', '192.168.1.1')
+
+ def test_switch_latest_stat(self):
+ self.do_latest_stat('switch', '00:01:02:03:04:05')
+
+class BasicEventsTest(StatsTestCase):
+
+ EVENTS_DATA = {
+ '192.168.1.1': [
+ {'timestamp': make_timestamp(1,0), 'component': 'sdnplatform', 'log-level': 'Error', 'message': 'Something bad happened'},
+ {'timestamp': make_timestamp(1,1), 'component': 'sdnplatform', 'log-level': 'Info', 'message': 'Something else happened', 'package': 'net.sdnplatformcontroller.core'},
+ {'timestamp': make_timestamp(1,4), 'component': 'sdnplatform', 'log-level': 'Info', 'message': 'Switch connected: 01:02:03:04:45:56', 'package': 'net.sdnplatformcontroller.core', 'dpid': '01:02:03:04:45:56'},
+ {'timestamp': make_timestamp(2,4), 'component': 'django', 'log-level': 'Info', 'message': 'GET: /rest/v1/model/foo'},
+ {'timestamp': make_timestamp(4,10), 'component': 'cassandra', 'log-level': 'Info', 'message': 'Compaction occurred'},
+ {'timestamp': make_timestamp(4,11), 'component': 'cassandra', 'log-level': 'Info', 'message': 'One more compaction occurred'},
+ {'timestamp': make_timestamp(7,10), 'component': 'cassandra', 'log-level': 'Info', 'message': 'Another compaction occurred'},
+ ]
+ }
+
+ TAGGED_EVENTS_DATA = {
+ '192.168.1.1': [
+ {'timestamp': make_timestamp(10,0), 'pk-tag':'1234', 'component': 'sdnplatform', 'log-level': 'Error', 'message': 'Something bad happened'},
+ {'timestamp': make_timestamp(10,1), 'pk-tag':'5678', 'component': 'sdnplatform', 'log-level': 'Info', 'message': 'Something else happened', 'package': 'net.sdnplatformcontroller.core'},
+ ]
+ }
+
+
+ def check_events_results(self, returned_results, expected_results, message=None):
+ self.assertEqual(expected_results, returned_results, message)
+ #self.assertEqual(len(returned_results), len(expected_results), message)
+ #for i in range(len(returned_results)):
+ # self.assertEqual(returned_results[i], expected_results[i])
+
+ def test_events(self):
+ response = test_put_rest_data(self.EVENTS_DATA, 'v1/events/data/default')
+ self.assertEqual(response.status_code, 200)
+
+ # Get all of the data
+ response = test_get_rest_data('v1/events/data/default/192.168.1.1', {'start-time':make_timestamp(1,0),'end-time':make_timestamp(7,10)})
+ self.assertEqual(response.status_code, 200)
+ results = simplejson.loads(response.content)
+ self.check_events_results(results, self.EVENTS_DATA['192.168.1.1'])
+
+ # Get just one days data
+ response = test_get_rest_data('v1/events/data/default/192.168.1.1', {'start-time':make_timestamp(1,0),'end-time':make_timestamp(1,4)})
+ self.assertEqual(response.status_code, 200)
+ results = simplejson.loads(response.content)
+ self.check_events_results(results, self.EVENTS_DATA['192.168.1.1'][:3])
+
+ # Get two day range
+ response = test_get_rest_data('v1/events/data/default/192.168.1.1', {'start-time':make_timestamp(1,2),'end-time':make_timestamp(4,11)})
+ self.assertEqual(response.status_code, 200)
+ results = simplejson.loads(response.content)
+ self.check_events_results(results, self.EVENTS_DATA['192.168.1.1'][2:6])
+
+ def test_tagged_events(self):
+ response = test_put_rest_data(self.TAGGED_EVENTS_DATA, 'v1/events/data/default')
+ self.assertEqual(response.status_code, 200)
+
+ response = test_get_rest_data('v1/events/data/default/192.168.1.1', {'start-time':make_timestamp(10,0),'end-time':make_timestamp(10,2),'include-pk-tag':'true'})
+ self.assertEqual(response.status_code, 200)
+ results = simplejson.loads(response.content)
+ self.check_events_results(results, self.TAGGED_EVENTS_DATA['192.168.1.1'])
+
+ def test_delete_events(self):
+ response = test_put_rest_data(self.EVENTS_DATA, 'v1/events/data/default')
+ self.assertEqual(response.status_code, 200)
+
+ # Delete all but the first 2 and last 2 events
+ response = test_delete_rest_data('v1/events/data/default/192.168.1.1', {
+ 'start-time': self.EVENTS_DATA['192.168.1.1'][2]['timestamp'],
+ 'end-time':self.EVENTS_DATA['192.168.1.1'][-3]['timestamp']})
+ self.assertEquals(response.status_code, 200)
+
+ response = test_get_rest_data('v1/events/data/default/192.168.1.1', {'start-time':make_timestamp(1,0),'end-time':make_timestamp(7,10)})
+ self.assertEqual(response.status_code, 200)
+ results = simplejson.loads(response.content)
+ self.check_events_results(results, self.EVENTS_DATA['192.168.1.1'][:2] + self.EVENTS_DATA['192.168.1.1'][-2:])
+