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:])
+