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
+