blob: f2c69e1ffa8c01e1e1a7ed07188659b55c766507 [file] [log] [blame]
#
# 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:])