srikanth | 116e6e8 | 2014-08-19 07:22:37 -0700 | [diff] [blame] | 1 | # |
| 2 | # Copyright (c) 2013 Big Switch Networks, Inc. |
| 3 | # |
| 4 | # Licensed under the Eclipse Public License, Version 1.0 (the |
| 5 | # "License"); you may not use this file except in compliance with the |
| 6 | # License. You may obtain a copy of the License at |
| 7 | # |
| 8 | # http://www.eclipse.org/legal/epl-v10.html |
| 9 | # |
| 10 | # Unless required by applicable law or agreed to in writing, software |
| 11 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
| 13 | # implied. See the License for the specific language governing |
| 14 | # permissions and limitations under the License. |
| 15 | # |
| 16 | |
| 17 | # Views for the application |
| 18 | # |
| 19 | |
| 20 | from django.http import HttpResponse |
| 21 | from django.shortcuts import render_to_response |
| 22 | from django.utils import simplejson |
| 23 | from sdncon.rest.jsonview import JsonResponse |
| 24 | from sdncon.apploader import AppLoader, AppLister |
| 25 | from sdncon.stats.views import init_db_connection |
| 26 | from sdncon.stats.data import get_log_event_data |
| 27 | import os |
| 28 | import time |
| 29 | import sdncon |
| 30 | |
| 31 | ComponentXlation = { |
| 32 | 'cassandra': 'database', |
| 33 | 'sdncon': 'sdncon', |
| 34 | 'sdnplatform': 'controller' |
| 35 | } |
| 36 | |
| 37 | def log_collection_enabled(config_file="%s/statd/statd.conf" % sdncon.SDN_ROOT): |
| 38 | import json |
| 39 | enabled = False |
| 40 | try: |
| 41 | with open(config_file, 'r') as fp: |
| 42 | conf = json.load(fp) |
| 43 | if conf.get('input'): |
| 44 | for i in conf['input']: |
| 45 | if i.get('name') == 'LogCollector' and i.get('modules'): |
| 46 | enabled = True |
| 47 | break; |
| 48 | except: |
| 49 | pass |
| 50 | return enabled |
| 51 | |
| 52 | def bsc_app_init(): |
| 53 | # Check if logging is enabled, else do not start the app |
| 54 | if not log_collection_enabled(): |
| 55 | return |
| 56 | |
| 57 | # By default, App Name is the same as directory name. Change if needed. |
| 58 | APP_NAME = os.path.dirname(__file__).split("/")[-1] |
| 59 | |
| 60 | # Create the App. Parameters are |
| 61 | # - Name: the id, lowercase letters only |
| 62 | # - Label: Human readable discription for the menu to the left |
| 63 | # - Priority: determines ranking the menu to the left), One-line description |
| 64 | # - Description: One line description of the app |
| 65 | app = AppLister(APP_NAME, "Logs", 5, "Controller Logs") |
| 66 | |
| 67 | # Add Tabs. Parameters are: |
| 68 | # - Name: the id, lowercase letters only |
| 69 | # - Label: Human readable discription for the menu to the left |
| 70 | # - View: name of the python function that contains the django view (see below) |
| 71 | app.addTab("controllerlogs", "Controller Logs", controller) |
| 72 | app.addTab("dblogs", "Database Logs", db) |
| 73 | app.addTab("sdnconlogs", "SDNCon Logs", sdncon) |
| 74 | app.addTab("alllogs", "All Logs", all) |
| 75 | AppLoader.addApp(app) |
| 76 | |
| 77 | # Views - functions that serve the HTML that goes into each tab |
| 78 | def controller(request): |
| 79 | return render_to_response('apps/logs/templates/logs.html', { 'source' : 'controller' } ) |
| 80 | |
| 81 | def db(request): |
| 82 | return render_to_response('apps/logs/templates/logs.html', { 'source' : 'db' } ) |
| 83 | |
| 84 | def sdncon(request): |
| 85 | return render_to_response('apps/logs/templates/logs.html', { 'source' : 'sdncon' } ) |
| 86 | |
| 87 | def all(request): |
| 88 | return render_to_response('apps/logs/templates/logs.html', { 'source' : 'all' } ) |
| 89 | |
| 90 | # Views defined in urls.py |
| 91 | def controller_log_data_all(request, cluster, controller): |
| 92 | init_db_connection() |
| 93 | time_now = time.time()*1000 #conver to ms |
| 94 | # we only get one day for now, this will be fixed when we move to an actually scalable solution |
| 95 | data_dict = get_log_event_data(cluster, controller, time_now - 86400000, time_now) |
| 96 | arr = get_array_from_dict(data_dict, True) |
| 97 | return generic_server_side_datatable(request, arr) |
| 98 | |
| 99 | def controller_log_data_controller(request, cluster, controller): |
| 100 | init_db_connection() |
| 101 | time_now = time.time()*1000 #conver to ms |
| 102 | # we only get one day for now, this will be fixed when we move to an actually scalable solution |
| 103 | data_dict = get_log_event_data(cluster, controller, time_now - 86400000, time_now) |
| 104 | filtered_dict = [] |
| 105 | for e in data_dict: |
| 106 | if e['component'] == 'sdnplatform' or e['component'] == 'sdnplatform.request': |
| 107 | filtered_dict.append(e) |
| 108 | arr = get_array_from_dict(filtered_dict, False) |
| 109 | return generic_server_side_datatable(request, arr) |
| 110 | |
| 111 | def controller_log_data_db(request, cluster, controller): |
| 112 | init_db_connection() |
| 113 | time_now = time.time()*1000 #conver to ms |
| 114 | # we only get one day for now, this will be fixed when we move to an actually scalable solution |
| 115 | data_dict = get_log_event_data(cluster, controller, time_now - 86400000, time_now) |
| 116 | filtered_dict = [] |
| 117 | for e in data_dict: |
| 118 | if e['component'] == 'cassandra': |
| 119 | filtered_dict.append(e) |
| 120 | arr = get_array_from_dict(filtered_dict, False) |
| 121 | return generic_server_side_datatable(request, arr) |
| 122 | |
| 123 | def controller_log_data_sdncon(request, cluster, controller): |
| 124 | init_db_connection() |
| 125 | time_now = time.time()*1000 #conver to ms |
| 126 | # we only get one day for now, this will be fixed when we move to an actually scalable solution |
| 127 | data_dict = get_log_event_data(cluster, controller, time_now - 86400000, time_now) |
| 128 | filtered_dict = [] |
| 129 | for e in data_dict: |
| 130 | if e['component'] == 'sdncon': |
| 131 | filtered_dict.append(e) |
| 132 | arr = get_array_from_dict(filtered_dict, False) |
| 133 | return generic_server_side_datatable(request, arr) |
| 134 | |
| 135 | |
| 136 | # Helper functions |
| 137 | def generic_server_side_datatable(request, data_array): |
| 138 | start = request.GET.get("iDisplayStart",0) |
| 139 | length = request.GET.get("iDisplayLength",10) |
| 140 | search_str = request.GET.get("sSearch",None) |
| 141 | |
| 142 | if search_str: |
| 143 | data_array_f = [] |
| 144 | for d in data_array: |
| 145 | for e in d: |
| 146 | try: |
| 147 | if e.find(search_str) > -1: |
| 148 | data_array_f.append(d) |
| 149 | break |
| 150 | except Exception: |
| 151 | pass |
| 152 | else: |
| 153 | data_array_f = data_array |
| 154 | |
| 155 | data_subset = data_array_f[int(start):int(start)+int(length)] |
| 156 | response_data = '{ "aaData":'+simplejson.dumps(data_subset) + ', "iTotalDisplayRecords": ' + str(len(data_array_f)) + ', "iTotalRecords": ' + str(len(data_array_f)) +'}' |
| 157 | return HttpResponse(response_data, 'application/json') |
| 158 | |
| 159 | # Takes a dictionary that includes |
| 160 | # { "timestamp": unix_ts, "level" : "string", "message": "msg" } |
| 161 | # and turns it into a 2D array that includes |
| 162 | # [ [unix_ts, level, message], [etc], [etc] ] |
| 163 | def get_array_from_dict(dict, includeComp): |
| 164 | array = [] |
| 165 | for element in reversed(dict): |
| 166 | if 'component' in element and element['component'] == 'sdnplatform.request': |
| 167 | entry = parse_sdnplatform_request_log_entry(element) |
| 168 | else: |
| 169 | entry = [] |
| 170 | if 'timestamp' in element: |
| 171 | entry.append(element['timestamp']) |
| 172 | else: |
| 173 | entry.append('-') |
| 174 | if 'log-level' in element: |
| 175 | entry.append(element['log-level']) |
| 176 | else: |
| 177 | entry.append('-') |
| 178 | if 'message' in element: |
| 179 | entry.append(element['message']) |
| 180 | else: |
| 181 | entry.append('-') |
| 182 | |
| 183 | if includeComp: |
| 184 | if 'component' in element: |
| 185 | entry.insert(1, ComponentXlation[element['component']]) |
| 186 | else: |
| 187 | entry.append(1, '-') |
| 188 | |
| 189 | array.append(entry) |
| 190 | return array |
| 191 | |
| 192 | # Parses a SDNPlatform request log entry into an array |
| 193 | # Format is similar to |
| 194 | # { |
| 195 | # 'package': 'Python-urllib/2.7', |
| 196 | # 'timestamp': 1306967011000L, |
| 197 | # 'component': 'sdnplatform.request', |
| 198 | # 'request': 'GET /wm/core/counter/00:00:00:00:00:73:28:02_OFPacketIn_L3_IPv4/json HTTP/1.1', |
| 199 | # 'responseLen': '37', |
| 200 | # 'client': '127.0.0.1', |
| 201 | # 'responseCode': '200' |
| 202 | # } |
| 203 | # Output array is [unix_ts, level, message] |
| 204 | def parse_sdnplatform_request_log_entry(element): |
| 205 | entry = [] |
| 206 | if 'timestamp' in element: |
| 207 | entry.append(element['timestamp']) |
| 208 | else: |
| 209 | entry.append('-') |
| 210 | if 'responseCode' in element: |
| 211 | rc = element['responseCode'] |
| 212 | if rc == '404' or rc == '500': |
| 213 | entry.append('ERROR') |
| 214 | elif rc == '200': |
| 215 | entry.append('INFO') |
| 216 | else: |
| 217 | entry.append('-') |
| 218 | else: |
| 219 | entry.append('-') |
| 220 | msg = '' |
| 221 | if 'request' in element: |
| 222 | msg += element['request'] |
| 223 | if 'client' in element: |
| 224 | msg += ' CLIENT ' + element['client'] |
| 225 | if 'responseCode' in element: |
| 226 | msg += ' ' + element['responseCode'] |
| 227 | if 'package' in element: |
| 228 | msg += ' ' + element['package'] |
| 229 | entry.append(msg) |
| 230 | return entry |
| 231 | |