Base net-virt CLI files on top of which ONOS specific changes will be done
diff --git a/cli/sdncon/stats/views.py b/cli/sdncon/stats/views.py
new file mode 100755
index 0000000..5172cc1
--- /dev/null
+++ b/cli/sdncon/stats/views.py
@@ -0,0 +1,364 @@
+#
+# 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 cassandra.ttypes import *
+from django.conf import settings
+from django.http import HttpResponse
+from django.utils import simplejson
+from functools import wraps
+import time
+from .data import StatsException, StatsInvalidStatsDataException, \
+ StatsInvalidStatsTypeException, \
+ get_stats_db_connection, init_stats_db_connection, \
+ get_stats_metadata, get_stats_type_index, \
+ get_stats_target_types, get_stats_targets, delete_stats_data, \
+ get_stats_data, get_latest_stat_data, put_stats_data, \
+ get_closest_sample_interval, get_closest_window_interval, \
+ get_log_event_data, put_log_event_data, delete_log_event_data, \
+ VALUE_DATA_FORMAT
+
+from sdncon.rest.views import RestException, \
+ RestInvalidPutDataException, RestMissingRequiredQueryParamException,\
+ RestInvalidMethodException, RestDatabaseConnectionException,\
+ RestInternalException, RestResourceNotFoundException, \
+ safe_rest_view, JSON_CONTENT_TYPE, get_successful_response
+from sdncon.controller.config import get_local_controller_id
+
+
+class RestStatsException(RestException):
+ def __init__(self, stats_exception):
+ super(RestStatsException,self).__init__('Error accessing stats: ' + str(stats_exception))
+
+class RestStatsInvalidTimeDurationUnitsException(RestException):
+ def __init__(self, units):
+ super(RestStatsInvalidTimeDurationUnitsException,self).__init__('Invalid time duration units: ' + str(units))
+
+
+class RestStatsInvalidTimeRangeException(RestException):
+ def __init__(self):
+ super(RestStatsInvalidTimeRangeException,self).__init__('Invalid time range specified in stats REST API. '
+ '2 out of 3 of start-time, end-time, and duration params must be specified.')
+
+
+@safe_rest_view
+def safe_stats_rest_view(func, *args, **kwargs):
+ try:
+ request = args[0]
+ response = func(*args, **kwargs)
+ except RestException:
+ raise
+ except StatsInvalidStatsDataException:
+ raise RestInvalidPutDataException()
+ except StatsInvalidStatsTypeException:
+ raise RestResourceNotFoundException(request.path)
+ except StatsException, e:
+ raise RestStatsException(e)
+ except Exception, e:
+ raise RestInternalException(e)
+ return response
+
+
+def safe_stats_view(func):
+ """
+ This is a decorator that takes care of exception handling for the
+ stats views so that stats exceptions are converted to the appropriate
+ REST exception.
+ """
+ @wraps(func)
+ def _func(*args, **kwargs):
+ response = safe_stats_rest_view(func, *args, **kwargs)
+ return response
+
+ return _func
+
+
+def init_db_connection():
+ db_connection = get_stats_db_connection()
+ if not db_connection:
+ try:
+ stats_db_settings = settings.STATS_DATABASE
+ except Exception:
+ stats_db_settings = {}
+
+ host = stats_db_settings.get('HOST', 'localhost')
+ port = stats_db_settings.get('PORT', 9160)
+ keyspace = stats_db_settings.get('NAME', 'sdnstats')
+ user = stats_db_settings.get('USER')
+ password = stats_db_settings.get('PASSWORD')
+ replication_factor = stats_db_settings.get('CASSANDRA_REPLICATION_FACTOR', 1)
+ column_family_def_default_settings = stats_db_settings.get('CASSANDRA_COLUMN_FAMILY_DEF_DEFAULT_SETTINGS', {})
+
+ init_stats_db_connection(host, port, keyspace, user, password, replication_factor, column_family_def_default_settings)
+
+ db_connection = get_stats_db_connection()
+ assert(db_connection is not None)
+
+START_TIME_QUERY_PARAM = 'start-time'
+END_TIME_QUERY_PARAM = 'end-time'
+DURATION_QUERY_PARAM = 'duration'
+SAMPLE_INTERVAL_QUERY_PARAM = 'sample-interval'
+SAMPLE_COUNT_QUERY_PARAM = 'sample-count'
+SAMPLE_WINDOW_QUERY_PARAM = 'sample-window'
+DATA_FORMAT_QUERY_PARAM = 'data-format'
+LIMIT_QUERY_PARAM = 'limit'
+INCLUDE_PK_TAG_QUERY_PARAM = 'include-pk-tag'
+
+DEFAULT_SAMPLE_COUNT = 50
+
+def convert_time_point(time_point):
+
+ if time_point is None:
+ return None
+
+ if time_point:
+ time_point = time_point.lower()
+ if time_point in ('now', 'current'):
+ time_point = int(time.time() * 1000)
+ else:
+ time_point = int(time_point)
+
+ return time_point
+
+
+UNIT_CONVERSIONS = (
+ (('h', 'hour', 'hours'), 3600000),
+ (('d', 'day', 'days'), 86400000),
+ (('w', 'week', 'weeks'), 604800000),
+ (('m', 'min', 'mins', 'minute', 'minutes'), 60000),
+ (('s', 'sec', 'secs', 'second', 'seconds'), 1000),
+ (('ms', 'millisecond', 'milliseconds'), 1)
+)
+
+def convert_time_duration(duration):
+
+ if duration is None:
+ return None
+
+ value = ""
+ for c in duration:
+ if not c.isdigit():
+ break
+ value += c
+
+ units = duration[len(value):].lower()
+ value = int(value)
+
+ if units:
+ converted_value = None
+ for conversion in UNIT_CONVERSIONS:
+ if units in conversion[0]:
+ converted_value = value * conversion[1]
+ break
+ if converted_value is None:
+ raise RestStatsInvalidTimeDurationUnitsException(units)
+
+ value = converted_value
+
+ return value
+
+
+def get_time_range(start_time, end_time, duration):
+
+ if not start_time and not end_time and not duration:
+ return (None, None)
+
+ start_time = convert_time_point(start_time)
+ end_time = convert_time_point(end_time)
+ duration = convert_time_duration(duration)
+
+ if start_time:
+ if not end_time and duration:
+ end_time = start_time + duration
+ elif end_time and duration:
+ start_time = end_time - duration
+
+ if not start_time or not end_time:
+ raise RestStatsInvalidTimeRangeException()
+
+ return (start_time, end_time)
+
+
+def get_time_range_from_request(request):
+ start_time = request.GET.get(START_TIME_QUERY_PARAM)
+ end_time = request.GET.get(END_TIME_QUERY_PARAM)
+ duration = request.GET.get(DURATION_QUERY_PARAM)
+
+ return get_time_range(start_time, end_time, duration)
+
+#def get_stats_time_range(request):
+# start_time = request.GET.get(START_TIME_QUERY_PARAM)
+# end_time = request.GET.get(END_TIME_QUERY_PARAM)
+#
+# if not start_time and not end_time:
+# return None
+#
+# if not start_time:
+# raise RestMissingRequiredQueryParamException(START_TIME_QUERY_PARAM)
+# if not end_time:
+# raise RestMissingRequiredQueryParamException(END_TIME_QUERY_PARAM)
+#
+# return (start_time, end_time)
+
+
+@safe_stats_view
+def do_get_stats(request, cluster, target_type, target_id, stats_type):
+
+ # FIXME: Hack to handle the old hard-coded controller id value
+ if target_type == 'controller' and target_id == 'localhost':
+ target_id = get_local_controller_id()
+
+ # Get the time range over which we're getting the stats
+ start_time, end_time = get_time_range_from_request(request)
+
+ init_db_connection()
+
+ if request.method == 'GET':
+ window = request.GET.get(SAMPLE_WINDOW_QUERY_PARAM, 0)
+ if window:
+ window = convert_time_duration(window)
+ if window != 0:
+ window = get_closest_window_interval(int(window))
+ # FIXME: Error checking on window value
+
+ data_format = request.GET.get(DATA_FORMAT_QUERY_PARAM, VALUE_DATA_FORMAT)
+ # FIXME: Error checking on data_format value
+
+ limit = request.GET.get(LIMIT_QUERY_PARAM)
+ if limit:
+ limit = int(limit)
+ # FIXME: Error checking on limit value
+
+ if start_time is not None and end_time is not None:
+ # FIXME: Error checking on start_time and end_time values
+ sample_interval = request.GET.get(SAMPLE_INTERVAL_QUERY_PARAM)
+ if not sample_interval:
+ # FIXME: Error checking on sample_period value
+ sample_count = request.GET.get(SAMPLE_COUNT_QUERY_PARAM, DEFAULT_SAMPLE_COUNT)
+ # FIXME: Error checking on sample_count value
+
+ sample_interval = (end_time - start_time) / int(sample_count)
+ else:
+ sample_interval = convert_time_duration(sample_interval)
+
+ if sample_interval != 0:
+ sample_interval = get_closest_sample_interval(sample_interval)
+
+ stats_data = get_stats_data(cluster, target_type, target_id,
+ stats_type, start_time, end_time, sample_interval, window, data_format, limit)
+ else:
+ stats_data = get_latest_stat_data(cluster, target_type, target_id, stats_type, window, data_format)
+
+ response_data = simplejson.dumps(stats_data)
+ response = HttpResponse(response_data, JSON_CONTENT_TYPE)
+
+ elif request.method == 'DELETE':
+ delete_stats_data(cluster, target_type, target_id, stats_type,
+ start_time, end_time)
+ response = get_successful_response()
+ else:
+ raise RestInvalidMethodException()
+
+ return response
+
+
+@safe_stats_view
+def do_get_stats_metadata(request, cluster, stats_type=None):
+ metadata = get_stats_metadata(cluster, stats_type)
+ response_data = simplejson.dumps(metadata)
+ return HttpResponse(response_data, JSON_CONTENT_TYPE)
+
+
+@safe_stats_view
+def do_get_stats_type_index(request, cluster, target_type, target_id, stats_type=None):
+ # FIXME: Hack to handle the old hard-coded controller id value
+ if target_type == 'controller' and target_id == 'localhost':
+ target_id = get_local_controller_id()
+ init_db_connection()
+ index_data = get_stats_type_index(cluster, target_type, target_id, stats_type)
+ response_data = simplejson.dumps(index_data)
+ return HttpResponse(response_data, JSON_CONTENT_TYPE)
+
+
+@safe_stats_view
+def do_get_stats_target_types(request, cluster):
+ init_db_connection()
+ target_type_data = get_stats_target_types(cluster)
+ response_data = simplejson.dumps(target_type_data)
+ return HttpResponse(response_data, JSON_CONTENT_TYPE)
+
+
+@safe_stats_view
+def do_get_stats_targets(request, cluster, target_type=None):
+ init_db_connection()
+ target_data = get_stats_targets(cluster, target_type)
+ response_data = simplejson.dumps(target_data)
+ return HttpResponse(response_data, JSON_CONTENT_TYPE)
+
+
+@safe_stats_view
+def do_put_stats(request, cluster):
+ if request.method != 'PUT':
+ raise RestInvalidMethodException()
+
+ init_db_connection()
+
+ stats_data = simplejson.loads(request.raw_post_data)
+ put_stats_data(cluster, stats_data)
+
+ response = get_successful_response()
+
+ return response
+
+
+@safe_stats_view
+def do_get_events(request, cluster, node_id):
+ # FIXME: Hack to handle the old hard-coded controller id value
+ if node_id == 'localhost':
+ node_id = get_local_controller_id()
+
+ # Get the time range over which we're getting the events
+ start_time, end_time = get_time_range_from_request(request)
+
+ init_db_connection()
+
+ if request.method == 'GET':
+ include_pk_tag_param = request.GET.get(INCLUDE_PK_TAG_QUERY_PARAM, 'false')
+ include_pk_tag = include_pk_tag_param.lower() == 'true'
+ events_list = get_log_event_data(cluster, node_id, start_time, end_time, include_pk_tag)
+ response_data = simplejson.dumps(events_list)
+ response = HttpResponse(response_data, JSON_CONTENT_TYPE)
+ elif request.method == 'DELETE':
+ delete_log_event_data(cluster, node_id, start_time, end_time)
+ response = get_successful_response()
+ else:
+ raise RestInvalidMethodException()
+
+ return response
+
+@safe_stats_view
+def do_put_events(request, cluster):
+ if request.method != 'PUT':
+ raise RestInvalidMethodException()
+
+ init_db_connection()
+
+ events_data = simplejson.loads(request.raw_post_data)
+ put_log_event_data(cluster, events_data)
+
+ response = get_successful_response()
+
+ return response
+