blob: 41b0acb94727997a4e85d007d48e1b0ba38fca69 [file] [log] [blame]
srikanth116e6e82014-08-19 07:22:37 -07001#
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
17from django.db.models import AutoField, BooleanField, IntegerField, FloatField, ForeignKey
18from django.forms import ValidationError
19from django.http import HttpResponse
20from django.utils import simplejson
21from functools import wraps
Srikanth Vavilapalli669ba8a2014-10-16 11:41:19 -070022#from sdncon.controller.models import Controller
srikanthf2c888b2014-08-20 10:46:55 -070023#from sdncon.controller.models import ControllerAlias, ControllerDomainNameServer, ControllerInterface, FirewallRule
Srikanth Vavilapalli669ba8a2014-10-16 11:41:19 -070024#from sdncon.controller.models import Switch, Port, PortAlias, Link
srikanth116e6e82014-08-19 07:22:37 -070025from sdncon.rest.models import UserData
26from sdncon.controller.notification import begin_batch_notification, end_batch_notification
27from django.views.decorators.csrf import csrf_exempt
28import base64
29import urllib
30import urllib2
31import sys
32import json
33import os
34import re
35import time
36import traceback
37import collections
38import uuid
39import subprocess
40from datetime import datetime
41from sdncon.controller.oswrapper import exec_os_wrapper
42from sdncon.controller.config import get_local_controller_id
43from sdncon.rest.config import config_check_state
44from sdncon.controller.oswrapper import get_system_version_string
45import sdncon
46
47TEXT_PLAIN_CONTENT_TYPE = 'text/plain'
48TEXT_JAVASCRIPT_CONTENT_TYPE = 'text/javascript'
49JSON_CONTENT_TYPE = 'application/json'
50BINARY_DATA_CONTENT_TYPE = 'application/octet-stream'
51
srikanthde50b092014-08-19 08:31:46 -070052onos = 1
53
54if onos == 1:
Srikanth Vavilapallif2add512014-09-12 11:58:15 -070055 #CONTROLLER_URL_PREFIX = 'http://localhost:9000/wm/'
56 CONTROLLER_URL_PREFIX = 'http://localhost:8080/wm/'
srikanthde50b092014-08-19 08:31:46 -070057else:
58 CONTROLLER_URL_PREFIX = 'http://localhost:8080/wm/'
srikanth116e6e82014-08-19 07:22:37 -070059
60def controller_url(*elements):
61 return CONTROLLER_URL_PREFIX + '/'.join(elements)
62
63class RestException(Exception):
64 pass
65
66class RestInvalidDataTypeException(RestException):
67 def __init__(self, name):
68 super(RestInvalidDataTypeException,self).__init__('Invalid data type: ' + name)
69
70class RestInvalidMethodException(RestException):
71 def __init__(self):
72 super(RestInvalidMethodException,self).__init__('Invalid HTTP method')
73
74class RestResourceNotFoundException(RestException):
75 def __init__(self, url):
76 super(RestResourceNotFoundException,self).__init__('Resource not found: ' + url)
77
78class RestDatabaseConnectionException(RestException):
79 def __init__(self):
80 super(RestDatabaseConnectionException,self).__init__('Error connecting to database')
81
82class RestAuthenticationRequiredException(RestException):
83 def __init__(self):
84 super(RestAuthenticationRequiredException,self).__init__('Authentication required')
85
86class RestInvalidQueryParameterException(RestException):
87 def __init__(self, param_name):
88 super(RestInvalidQueryParameterException, self).__init__('Invalid query parameter: ' + str(param_name))
89
90class RestInvalidFilterParameterException(RestException):
91 def __init__(self, param_name):
92 super(RestInvalidFilterParameterException, self).__init__('Filter query parameters not allowed when URL contains resource iD: ' + str(param_name))
93
94class RestNoListResultException(RestException):
95 def __init__(self):
96 super(RestNoListResultException, self).__init__('The query result must be a single instance if the "nolist" query param is set')
97
98class RestInvalidPutDataException(RestException):
99 def __init__(self):
100 super(RestInvalidPutDataException, self).__init__('The request data for a PUT request must be a JSON dictionary object')
101
102class RestMissingRequiredQueryParamException(RestException):
103 def __init__(self, param_name):
104 super(RestMissingRequiredQueryParamException, self).__init__('Missing required query parameter: ' + str(param_name))
105
106class RestValidationException(RestException):
107 def __init__(self, model_error=None, field_errors=None):
108 # Build the exception message from model error and field errors
109 message = 'Validation error'
110 if model_error:
111 message = message + '; ' + model_error
112 if field_errors:
113 message += '; invalid fields: {'
114 first_time = True
115 for field_name, field_message in field_errors.items():
116 if not first_time:
117 message += '; '
118 else:
119 first_time = False
120 message = message + field_name + ': ' + field_message
121
122 message += '}'
123
124 super(RestValidationException, self).__init__(message)
125 self.model_error = model_error
126 self.field_errors = field_errors
127
128class RestModelException(RestException):
129 def __init__(self, exc):
130 super(RestModelException, self).__init__('Error: ' + str(exc))
131
132class RestSaveException(RestException):
133 def __init__(self, exc):
134 super(RestSaveException, self).__init__('Error saving data: ' + str(exc))
135
136class RestInvalidOrderByException(RestException):
137 def __init__(self,field_name):
138 super(RestInvalidOrderByException, self).__init__('Invalid orderby field: ' + field_name)
139
140class RestInternalException(RestException):
141 def __init__(self, exc):
142 super(RestInternalException,self).__init__('Unknown REST error: ' + unicode(exc))
143
144class RestUpgradeException(RestException):
145 def __init__(self, exc):
146 super(RestUpgradeException, self).__init__('Error: ' + str(exc))
147
148class RestProvisionException(RestException):
149 def __init__(self, exc):
150 super(RestProvisionException, self).__init__('Error: ' + str(exc))
151
152class RestDecommissionException(RestException):
153 def __init__(self, exc):
154 super(RestDecommissionException, self).__init__('Error: ' + str(exc))
155
156class RestInvalidLog(RestException):
157 def __init__(self, exc):
158 super(RestInvalidLog, self).__init__('Error: ' + str(exc))
159
160def handle_validation_error(model_info, validation_error):
161 model_error = None
162 field_errors = None
163 if hasattr(validation_error, 'message_dict'):
164 # The field errors we get in the ValidationError are a bit different
165 # then what we want for the RestValidationException. First, we
166 # need to convert the Django field name to the (possibly renamed)
167 # REST field name. Second, the per-field error message is possibly a
168 # list of messages, which we concatenate into a single string for the
169 # RestValidationException
170 for field_name, field_message in validation_error.message_dict.items():
171 if type(field_message) in (list, tuple):
172 converted_field_message = ''
173 for msg in field_message:
174 converted_field_message = converted_field_message + msg + ' '
175 else:
176 converted_field_message += unicode(field_message)
177 if field_name == '__all__':
178 model_error = converted_field_message
179 else:
180 if not field_errors:
181 field_errors = {}
182 field_info = model_info.field_name_dict.get(field_name)
183 if field_info:
184 field_errors[field_info.rest_name] = converted_field_message
185 else:
186 field_errors[field_name] = 'Private field invalid; ' + converted_field_message
187 elif hasattr(validation_error, 'messages'):
188 model_error = ':'.join(validation_error.messages)
189 else:
190 model_error = str(validation_error)
191 raise RestValidationException(model_error, field_errors)
192
193def get_successful_response(description=None, status_code=200):
194 content = get_successful_response_data(description)
195 return HttpResponse(content, JSON_CONTENT_TYPE, status_code)
196
197def get_successful_response_data(description = 'success'):
198 obj = {'description': description}
199 return simplejson.dumps(obj)
200
201def get_sdnplatform_response(url, timeout = None):
202
203 try:
204 response_text = urllib2.urlopen(url, timeout=timeout).read()
205 return HttpResponse(response_text, JSON_CONTENT_TYPE)
206 except urllib2.HTTPError, e:
207 response_text = e.read()
208 response = simplejson.loads(response_text)
209 response['error_type'] = "SDNPlatformError"
210 return HttpResponse(content=simplejson.dumps(response),
211 status=e.code,
212 content_type=JSON_CONTENT_TYPE)
213
214def get_sdnplatform_query(request, path):
215 """
216 This returns controller-level storage table list
217 """
218 if request.method != 'GET':
219 raise RestInvalidMethodException()
220 url = controller_url(path) + '/?%s' % request.META['QUERY_STRING']
221 return get_sdnplatform_response(url)
222
223def safe_rest_view(func):
224 """
225 This is a decorator that takes care of exception handling for the
226 REST views so that we return an appropriate error HttpResponse if
227 an exception is thrown from the view
228 """
229 @wraps(func)
230 def _func(*args, **kwargs):
231 try:
232 response = func(*args, **kwargs)
233 except Exception, exc:
234 end_batch_notification(True)
235 if not isinstance(exc, RestException):
236 # traceback.print_exc()
237 exc = RestInternalException(exc)
238 response_obj = {'error_type': exc.__class__.__name__, 'description': unicode(exc)}
239 if isinstance(exc, RestValidationException):
240 if exc.model_error:
241 response_obj['model_error'] = exc.model_error
242 if exc.field_errors:
243 response_obj['field_errors'] = exc.field_errors
244 content = simplejson.dumps(response_obj)
245 content_type = JSON_CONTENT_TYPE
246
247 if isinstance(exc, RestInvalidMethodException):
248 status_code = 405
249 elif isinstance(exc, RestResourceNotFoundException):
250 status_code = 404
251 elif isinstance(exc, RestInternalException):
252 status_code = 500
253 else:
254 status_code = 400
255 response = HttpResponse(content, content_type, status_code)
256 if isinstance(exc, RestInvalidMethodException):
257 response['Allow'] = "GET, PUT, DELETE"
258 return response
259 return _func
260
261rest_model_info_dict = {}
262
263class RestFieldInfo(object):
264 def __init__(self, name, django_field_info, hidden=False,
265 rest_name=None, json_serialize=False):
266 self.name = name
267 self.django_field_info = django_field_info
268 self.rest_name = rest_name
269 self.json_serialize = json_serialize
270
271class RestModelInfo(object):
272 def __init__(self, rest_name, model_class):
273 self.rest_name = rest_name
274 self.model_class = model_class
275 self.primary_key = None
276 self.field_name_dict = {}
277 self.rest_name_dict = {}
278
279 for field in model_class._meta.local_fields:
280 field_name = field.name
281 rest_name = field.name
282 if field.primary_key:
283 self.primary_key = field_name
284 # TODO: Are there other field types that should be included here?
285 json_serialize = type(field) not in (AutoField, BooleanField, IntegerField, FloatField)
286 self.set_field_info(field_name, rest_name, field, json_serialize)
287
288
289 # this is how a RestFieldInfo is created - pass in django_field_info
290 def get_field_info(self, field_name, django_field_info=None):
291 field_info = self.field_name_dict.get(field_name)
292 if not field_info and django_field_info:
293 field_info = RestFieldInfo(field_name, django_field_info)
294 self.field_name_dict[field_name] = field_info
295 return field_info
296
297 def hide_field(self, field_name):
298 field_info = self.get_field_info(field_name)
299 del self.field_name_dict[field_name]
300 del self.rest_name_dict[field_info.rest_name]
301
302 def set_field_info(self, field_name, rest_name, django_field_info, json_serialize=None):
303 field_info = self.get_field_info(field_name, django_field_info)
304 if field_info.rest_name in self.rest_name_dict:
305 del self.rest_name_dict[field_info.rest_name]
306 field_info.rest_name = rest_name
307 if json_serialize != None:
308 field_info.json_serialize = json_serialize
309 self.rest_name_dict[rest_name] = field_info
310
311def get_rest_model_info(name):
312 return rest_model_info_dict[name]
313
314def add_rest_model_info(info):
315 if rest_model_info_dict.get(info.rest_name):
316 raise RestException('REST model info already exists')
317 rest_model_info_dict[info.rest_name] = info
318
319rest_initialized = False
320
321def get_default_rest_name(model):
322 # TODO: Ideally should do something a bit smarter here.
323 # Something like convert from camel-case class names to hyphenated names:
324 # For example:
325 # MyTestClass => my-test-class
326 # MyURLClass => my-url-class
327 #
328 # This isn't super-important for now, since you can set it explicitly
329 # with the nested Rest class.
330 return model.__name__.lower()
331
332def initialize_rest():
333 global rest_initialized
334 if rest_initialized:
335 return
336
337 from django.db.models import get_models
338 for model in get_models():
339
340 # If the model class has a nested class named 'Rest' then that means
341 # the model should be exposed in the REST API.
342 if hasattr(model, 'Rest'):
343 # By default the REST API uses the lower-case-converted name
344 # of the model class as the name in the REST URL, but this can
345 # be overridden by defining the 'NAME' attribute in the Rest class.
346 if hasattr(model.Rest, 'NAME'):
347 rest_name = model.Rest.NAME
348 else:
349 rest_name = get_default_rest_name(model)
350
351 if model._meta.proxy:
352 # This is a proxy class, drop through to the real one
353 base_model = model._meta.proxy_for_model
354 else:
355 base_model = model
356
357 # OK, we have the basic REST info, so we can create the info class
358 rest_model_info = RestModelInfo(rest_name, base_model)
359
360 # Check if there are any private or renamed fields
361 if hasattr(model.Rest, 'FIELD_INFO'):
362 for field_info in model.Rest.FIELD_INFO:
363 field_name = field_info['name']
364 rest_field_info = rest_model_info.get_field_info(field_name)
365 # Check if field exists in models - don't allow field only here in FIELD_INFO)
366 if not rest_field_info:
367 # LOOK! This field only exists in FIELD_INFO - skip
368 print "ERROR: %s for %s only in FIELD_INFO" % (field_name, rest_name)
369 continue
370
371 if field_info.get('private', False):
372 rest_model_info.hide_field(field_name)
373 else:
374 rest_name = field_info.get('rest_name')
375 if rest_name:
376 rest_model_info.set_field_info(field_name, rest_name, rest_field_info.django_field_info)
377
378 # Finished setting it up, so now add it to the list
379 add_rest_model_info(rest_model_info)
380
381 rest_initialized = True
382
383initialize_rest()
384
385@safe_rest_view
386def do_model_list(request):
387 """
388 This returns the list of models available in the REST API.
389 """
390
391 json_model_list = []
392 for model_name in rest_model_info_dict.keys():
393 json_model_info = {}
394 json_model_info["name"] = model_name
395 json_model_info["url_path"] = "rest/v1/model/" + model_name + "/"
396 json_model_list.append(json_model_info)
397
398 json_data = simplejson.dumps(json_model_list)
399 return HttpResponse(json_data, JSON_CONTENT_TYPE)
400
401@safe_rest_view
402def do_realtimestats(request, stattype, dpid):
403 """
404 This returns realtime statistics (flows, ports, table, aggregate,
405 desc, ...) for a dpid by calling the localhost sdnplatform
406 """
Fahad Naeem Khan0c1c7b32014-10-07 16:22:23 -0700407 #raise RestInvalidMethodException()
srikanth116e6e82014-08-19 07:22:37 -0700408 if request.method != 'GET':
409 raise RestInvalidMethodException()
Srikanth Vavilapallice98ea02014-09-28 22:36:32 -0700410 #url = controller_url('core', 'switch', dpid, stattype, 'json')
Srikanth Vavilapalli574f2702014-10-07 07:32:06 -0700411 if stattype == 'group':
412 stattype = 'groupStats'
Srikanth Vavilapalli8dff81f2014-10-07 14:25:32 -0700413 if stattype == 'groupdesc':
414 stattype = 'groupDesc'
Srikanth Vavilapallice98ea02014-09-28 22:36:32 -0700415 url = "http://localhost:8080/wm/floodlight/core/switch/%s/%s/json" % (dpid, stattype)
Fahad Naeem Khand563af62014-10-08 17:37:25 -0700416 return get_sdnplatform_response(url)
417
418@safe_rest_view
419def do_tablerealtimestats(request, tabletype, dpid):
420 """
421 This returns realtime statistics per table (flows (only)
422 current implementation) for a dpid by calling the localhost sdnplatform
423 """
424 #raise RestInvalidMethodException()
425 if request.method != 'GET':
426 raise RestInvalidMethodException()
427 #url = controller_url('core', 'switch', dpid, stattype, 'json')
428 url = "http://localhost:8080/wm/floodlight/core/switch/%s/table/%s/flow/json" % (dpid,tabletype)
Fahad Naeem Khan0c1c7b32014-10-07 16:22:23 -0700429 #url ="http://localhost:8080/wm/floodlight/core/switch/00:00:00:00:00:00:00:01/flow/json"
srikanth116e6e82014-08-19 07:22:37 -0700430 return get_sdnplatform_response(url)
431
432@safe_rest_view
433def do_sdnplatform_realtimestats(request, stattype, dpid=None, portid=None):
434 """
435 This returns realtime statistics from sdnplatform
436 """
437 if request.method != 'GET':
438 raise RestInvalidMethodException()
439 if dpid == None:
440 url = controller_url('core', 'counter', stattype, 'json')
441 elif portid == None:
442 url = controller_url('core', 'counter', dpid, stattype, 'json')
443 else:
444 url = controller_url('core', 'counter', dpid, portid, stattype, 'json')
445 return get_sdnplatform_response(url)
446
447@safe_rest_view
448def do_topology_tunnel_verify(request, srcdpid=None, dstdpid=None):
449 """
450 This initiates a liveness detection of tunnels.
451 """
452 if request.method != 'GET':
453 raise RestInvalidMethodException()
454
455 urlstring = srcdpid + '/' + dstdpid
456 url = controller_url('topology/tunnelverify', urlstring, 'json')
457
458 response_text = urllib2.urlopen(url).read()
459 time.sleep(4)
460 return do_topology_tunnel_status(request, srcdpid, dstdpid)
461
462@safe_rest_view
463def do_topology_tunnel_status(request, srcdpid='all', dstdpid='all'):
464 """
465 This returns the list of tunnels that have failed over the last observation interval.
466 """
467 if request.method != 'GET':
468 raise RestInvalidMethodException()
469
470 urlstring = srcdpid + '/' + dstdpid
471 url = controller_url('topology/tunnelstatus', urlstring, 'json')
472 response_text = urllib2.urlopen(url).read()
473 return HttpResponse(response_text, JSON_CONTENT_TYPE)
474
475@safe_rest_view
476def do_sdnplatform_realtimestatus(request, category=None, subcategory=None, srcdpid=None, dstdpid = None):
477 """
478 This returns realtime status of sdnplatform
479 """
480
481 if request.method != 'GET':
482 raise RestInvalidMethodException()
483
484 response_text = None
485 url = None
486 if category == 'network':
487 if subcategory == 'cluster':
488 url = controller_url('topology', 'switchclusters', 'json')
489 if subcategory == 'externalports':
490 url = controller_url('topology', 'externalports', 'json')
491 if subcategory == 'tunnelverify':
492 urlstring = subcategory+ '/' + srcdpid + '/' + dstdpid
493 url = controller_url('topology', urlstring, 'json')
494 if subcategory == 'tunnelstatus':
495 url = controller_url('topology', 'tunnelstatus', 'json')
496
497 if url:
498 response_text = urllib2.urlopen(url).read()
499 return HttpResponse(response_text, JSON_CONTENT_TYPE)
500
501@safe_rest_view
502def do_sdnplatform_realtimetest(http_request, category=None, subcategory=None):
503 """
504 This does a realtime test by sending an "explain packet" as packet in
505 and collecting the operations performed on the packet
506 """
507
508 if http_request.method != 'PUT':
509 raise RestInvalidMethodException()
510
511 response_text = None
512 url = None
513 if category == 'network':
514 if subcategory == 'explain-packet':
515 # set up the sdnplatform URL for explain packet (at internal port 8080
516 url = controller_url('vns', 'explain-packet', 'json')
517 post_data = http_request.raw_post_data
518 request = urllib2.Request(url, post_data)
519 request.add_header('Content-Type', 'application/json')
520 response = urllib2.urlopen(request)
521 response_text = response.read()
522 elif subcategory == 'path':
523 post_data = json.loads(http_request.raw_post_data)
524 url = controller_url('topology', 'route',
525 post_data['src-switch'],
526 str(post_data['src-switch-port']),
527 post_data['dst-switch'],
528 str(post_data['dst-switch-port']),
529 'json')
530
531 return get_sdnplatform_response(url)
532
533 return HttpResponse(response_text, JSON_CONTENT_TYPE)
534
535@safe_rest_view
536def do_sdnplatform_performance_monitor(http_request, category=None,
537 subcategory=None, type='all'):
538 """
539 This API returns performance related information from the sdnplatform and
540 sdnplatform components
541 """
542
543 if http_request.method != 'GET':
544 raise RestInvalidMethodException()
545
546 response_text = None
547 url = None
548 if category == 'performance-monitor':
549 # set up the sdnplatform URL for explain packet (at internal port 8080
550 url = controller_url('performance', type, 'json')
551 request = urllib2.Request(url)
552 response = urllib2.urlopen(request)
553 response_text = response.read()
554 return HttpResponse(response_text, JSON_CONTENT_TYPE)
555
556@safe_rest_view
557def do_sdnplatform_internal_debugs(http_request, category=None, subcategory=None,
558 query='all', component='device-manager'):
559 """
560 This API returns debugging related information from the sdnplatform and
561 sdnplatform components
562 """
563
564 if http_request.method != 'GET':
565 raise RestInvalidMethodException()
566
567 response_text = None
568 url = None
569 if category == 'internal-debugs':
570 # set up the sdnplatform URL for explain packet (at internal port 8080
571 url = controller_url('vns', 'internal-debugs', component, query, 'json')
572 request = urllib2.Request(url)
573 response = urllib2.urlopen(request)
574 response_text = response.read()
575 return HttpResponse(response_text, JSON_CONTENT_TYPE)
576
577@safe_rest_view
578def do_sdnplatform_event_history(http_request, category=None, subcategory=None,
579 evHistName='all', count='100'):
580 """
581 This API returns real-time event-history information from the sdnplatform and
582 sdnplatform components
583 """
584
585 if http_request.method != 'GET':
586 raise RestInvalidMethodException()
587
588 response_text = None
589 url = None
590 if category == 'event-history':
591 # set up the sdnplatform URL for explain packet (at internal port 8080
592 url = controller_url('core', 'event-history', evHistName, count, 'json')
593 request = urllib2.Request(url)
594 response = urllib2.urlopen(request)
595 response_text = response.read()
596 return HttpResponse(response_text, JSON_CONTENT_TYPE)
597
598@safe_rest_view
599def do_flow_cache(http_request, category=None, subcategory=None,
600 applName='None', applInstName='all', queryType='all'):
601 """
602 This API returns real-time event-history information from the sdnplatform and
603 sdnplatform components
604 """
605
606 if http_request.method != 'GET':
607 raise RestInvalidMethodException()
608
609 response_text = None
610 url = None
611 if category == 'flow-cache':
612 # set up the sdnplatform URL for explain packet (at internal port 8080
613 url = controller_url('vns', 'flow-cache', applName, applInstName, queryType, 'json')
614 request = urllib2.Request(url)
615 response = urllib2.urlopen(request)
616 response_text = response.read()
617 return HttpResponse(response_text, JSON_CONTENT_TYPE)
618
619
620@safe_rest_view
621def do_vns_realtimestats_flow(http_request, category=None, vnsName="all"):
622 """
623 This gets realtime flows for one or more vnses
624 """
625
626 if http_request.method != 'GET':
627 raise RestInvalidMethodException()
628
629 # set up the sdnplatform URL for per-vns flow (at internal port 8080
630 url = controller_url('vns', 'flow', vnsName, 'json')
631 return get_sdnplatform_response(url)
632
633@safe_rest_view
634def do_sdnplatform_counter_categories(request, stattype, layer, dpid=None, portid=None):
635 """
636 This returns counter categories from sdnplatform
637 """
638 if request.method != 'GET':
639 raise RestInvalidMethodException()
640 if dpid == None:
641 url = controller_url('core', 'counter', 'categories', stattype, layer, 'json')
642 elif portid == None:
643 url = controller_url('core', 'counter', 'categories', dpid, stattype, layer, 'json')
644 else:
645 url = controller_url('core', 'counter', 'categories', dpid, portid, stattype, layer, 'json')
646
647 return get_sdnplatform_response(url)
648
649@safe_rest_view
650@csrf_exempt
651def do_packettrace(request):
652 """
653 This sets a packet trace in sdnplatform.
654 period:
655 . >0 starts a trace session with the period
656 . =0 starts a trace session with no timeout
657 . <0 ends an ongoing session
658
659 The request method has to be POST since each request gets an unique sessionID
660 """
661 SESSIONID = 'sessionId'
662 sessionId = ""
663 filter = ""
664 if request.method != 'POST':
665 raise RestInvalidMethodException()
666
667 url = 'http://localhost:8080/wm/vns/packettrace/json'
668 request = urllib2.Request(url, request.raw_post_data, {'Content-Type':'application/json'})
669 try:
670 response = urllib2.urlopen(request)
671 response_text = response.read()
672 except Exception, e:
673 # SDNPlatform may not be running, but we don't want that to be a fatal
674 # error, so we just ignore the exception in that case.
675 pass
676
677 #response_data = json.loads(response_text)
678 #if SESSIONID in response_data:
679 # sessionId = response_data[SESSIONID]
680 #response_text = {SESSIONID:sessionId}
681
682 return HttpResponse(response_text, mimetype=JSON_CONTENT_TYPE)
683
684@safe_rest_view
685def do_controller_stats(request, stattype):
686 """
687 This returns controller-level statistics/info from sdnplatform
688 """
689 if request.method != 'GET':
690 raise RestInvalidMethodException()
691 url = 'http://127.0.0.1:8080/wm/core/controller/%s/json' % stattype
692 return get_sdnplatform_response(url)
693
694@safe_rest_view
695def do_controller_storage_table_list(request):
696 """
697 This returns controller-level storage table list
698 """
699 if request.method != 'GET':
700 raise RestInvalidMethodException()
701 url = 'http://127.0.0.1:8080/wm/core/storage/tables/json'
702 return get_sdnplatform_response(url)
703
704@safe_rest_view
705def do_device(request):
srikanthde50b092014-08-19 08:31:46 -0700706 if onos == 0:
707 return get_sdnplatform_query(request, "device")
708 else:
709 url = controller_url("onos", "topology", "hosts")
710 if request.META['QUERY_STRING']:
711 url += '?' + request.META['QUERY_STRING']
712 return get_sdnplatform_response(url)
srikanth116e6e82014-08-19 07:22:37 -0700713
714@safe_rest_view
715def do_switches(request):
srikanthde50b092014-08-19 08:31:46 -0700716 if onos == 0:
717 url = controller_url("core", "controller", "switches", "json")
718 else:
719 url = controller_url("onos", "topology", "switches")
srikanth116e6e82014-08-19 07:22:37 -0700720 if request.META['QUERY_STRING']:
721 url += '?' + request.META['QUERY_STRING']
722 return get_sdnplatform_response(url)
723
724@safe_rest_view
Fahad Naeem Khan5b558f22014-10-16 10:35:20 -0700725def do_routers(request):
726 if onos == 0:
727 url = controller_url("core", "controller", "switches", "json")
728 else:
729 url = controller_url("onos","segmentrouting", "routers")
730 if request.META['QUERY_STRING']:
731 url += '?' + request.META['QUERY_STRING']
732 return get_sdnplatform_response(url)
733
734@safe_rest_view
Fahad Naeem Khanee5c15a2014-10-17 19:13:51 -0700735def do_router_stats(request, stattype, dpid):
736 """
737 This returns the subnets info about the specifed
738 router dpid. statetype should be 'port'.
739 """
740 #raise RestInvalidMethodException()
741 if request.method != 'GET':
742 raise RestInvalidMethodException()
743 #url = controller_url('core', 'switch', dpid, stattype, 'json')
744 url = "http://localhost:8080/wm/onos/segmentrouting/router/%s/%s" % (dpid, stattype)
745 #raise RestInvalidMethodException(url)
746 #url = "http://localhost:8080/wm/onos/segementrouting/router/00:00:00:00:00:00:00:01/port"
747 return get_sdnplatform_response(url)
748
749@safe_rest_view
Fahad Naeem Khan1f8fb2e2014-09-25 16:39:18 -0700750def do_mastership(request):
751 url = controller_url("onos", "registry", "switches" ,"json")
752 #url = "http://127.0.0.1:8080/wm/onos/registry/switches/json"
753 if request.META['QUERY_STRING']:
754 url += '?' + request.META['QUERY_STRING']
Fahad Naeem Khan99ce8062014-10-01 11:03:54 -0700755 return get_sdnplatform_response(url)
756
757@safe_rest_view
758def do_controller(request):
759 url = controller_url("onos", "registry", "controllers" ,"json")
760 #url = "http://127.0.0.1:8080/wm/onos/registry/switches/json"
761 if request.META['QUERY_STRING']:
762 url += '?' + request.META['QUERY_STRING']
Fahad Naeem Khan1f8fb2e2014-09-25 16:39:18 -0700763 return get_sdnplatform_response(url)
764#'''
765
766@safe_rest_view
srikanth116e6e82014-08-19 07:22:37 -0700767def do_links(request):
srikanthde50b092014-08-19 08:31:46 -0700768 if onos == 0:
769 url = controller_url("topology", "links", "json")
770 else:
771 url = controller_url("onos", "topology", "links")
srikanth116e6e82014-08-19 07:22:37 -0700772 if request.META['QUERY_STRING']:
773 url += '?' + request.META['QUERY_STRING']
774 return get_sdnplatform_response(url)
775
776@safe_rest_view
777def do_vns_device_interface(request):
778 return get_sdnplatform_query(request, "vns/device-interface")
779
780@safe_rest_view
781def do_vns_interface(request):
782 return get_sdnplatform_query(request, "vns/interface")
783
784@safe_rest_view
785def do_vns(request):
786 return get_sdnplatform_query(request, "vns")
787
788@safe_rest_view
789def do_system_version(request):
790 if request.method != 'GET':
791 raise RestInvalidMethodException()
792 version = get_system_version_string()
793 response_text = simplejson.dumps([{ 'controller' : version }])
794 return HttpResponse(response_text, JSON_CONTENT_TYPE)
795
796available_log_files = {
797 'syslog' : '/var/log/syslog',
798 'sdnplatform' : '/opt/sdnplatform/sdnplatform/log/sdnplatform.log',
799 'console-access' : '/opt/sdnplatform/con/log/access.log',
800 'cassandra' : '/opt/sdnplatform/db/log/system.log',
801 'authlog' : '/var/log/auth.log',
802 'pre-start' : '/tmp/pre-start',
803 'post-start' : '/tmp/post-start',
804 # 'ftp' : '/var/log/ftp.log',
805}
806
807available_log_commands = {
808 'dmesg' : 'dmesg',
809 'process' : 'ps lax'
810}
811
812@safe_rest_view
813def do_system_log_list(request):
814 if request.method != 'GET':
815 raise RestInvalidMethodException()
816 existing_logs = []
817 for (log_name, log_path) in available_log_files.items():
818 try:
819 log_file = open(log_path, 'r')
820 existing_logs.append({ 'log' : log_name })
821 log_file.close()
822 except Exception, e:
823 pass
824
825 print '??'
826 for log_name in available_log_commands.keys():
827 print 'ADD', log_name
828 existing_logs.append({ 'log' : log_name })
829 response_text = simplejson.dumps(existing_logs)
830 return HttpResponse(response_text, JSON_CONTENT_TYPE)
831
832
833def generate_subprocess_output(cmd):
834
835 process = subprocess.Popen(cmd, shell=True,
836 stdout=subprocess.PIPE,
837 stderr=subprocess.STDOUT,
838 bufsize=1)
839 while True:
840 line = process.stdout.readline()
841 if line != None and line != "":
842 yield line
843 else:
844 break
845
846
847@safe_rest_view
848def do_system_log(request, log_name):
849 if request.method != 'GET':
850 raise RestInvalidMethodException()
851 print 'do system log', log_name
852
853 # manage command ouput differently
854 if log_name in available_log_commands:
855 cmd = available_log_commands[log_name]
856 print 'DOING COMMAND', cmd
857
858 return HttpResponse(generate_subprocess_output(cmd),
859 TEXT_PLAIN_CONTENT_TYPE)
860 return
861
862 log_path = available_log_files.get(log_name)
863 if log_name == None:
864 raise RestInvalidLog('No such log: %s' % log_name)
865
866 try:
867 log_file = open(log_path, 'r')
868 except Exception,e:
869 raise RestInvalidLog('Log does not exist: %s' % log_name)
870
871 # use a generator so that the complete log is not ever held in memory
872 def response(log_name, file):
873 for line in file:
874 yield line
875 file.close()
876
877 return HttpResponse(response(log_name, log_file), TEXT_PLAIN_CONTENT_TYPE)
878
879
880@safe_rest_view
881def do_system_uptime(request):
882 if request.method != 'GET':
883 raise RestInvalidMethodException()
884 url = controller_url('core', 'system', 'uptime', 'json')
885 return get_sdnplatform_response(url)
886
887
888def _collect_system_interfaces(lo = False):
889 from netifaces import interfaces, ifaddresses, AF_INET, AF_LINK
890 result = []
891 for iface in interfaces():
892 if iface.startswith('lo') and not lo:
893 continue # ignore loopback
894 addrs = ifaddresses(iface)
895 if AF_INET in addrs:
896 for addr in ifaddresses(iface)[AF_INET]:
897 result.append({'name' : iface,
898 'addr' : addr.get('addr', ''),
899 'netmask' : addr.get('netmask', ''),
900 'broadcast' : addr.get('broadcast', ''),
901 'peer' : addr.get('peer', '')})
902 return result
903
904
905def do_system_inet4_interfaces(request):
906 if request.method != 'GET':
907 raise RestInvalidMethodException()
908 response_text = simplejson.dumps(_collect_system_interfaces(lo = True))
909 return HttpResponse(response_text, JSON_CONTENT_TYPE)
910
911
912@safe_rest_view
913def do_system_time_zone_strings(request, list_type):
914 import pytz
915 if list_type == 'common':
916 string_list = pytz.common_timezones
917 elif list_type == "all":
918 string_list = pytz.all_timezones
919 else:
920 raise RestResourceNotFoundException(request.path)
921
922 response_text = simplejson.dumps(string_list)
923
924 return HttpResponse(response_text, JSON_CONTENT_TYPE)
925
926@safe_rest_view
927def do_check_config(request):
928 config_check_state()
929 return get_successful_response('checked config')
930
931@safe_rest_view
932def do_local_controller_id(request):
933 if request.method == 'GET':
934 controller_id = get_local_controller_id()
935 if not controller_id:
936 raise Exception("Unspecified local controller id")
937 response_text = simplejson.dumps({'id': controller_id})
938 elif request.method == 'PUT':
939 put_data = json.loads(request.raw_post_data)
940 controller_id = put_data.get('id')
941 _result = exec_os_wrapper("ControllerId", 'set', [controller_id])
942 response_text = get_successful_response_data('updated')
943 else:
944 raise RestInvalidMethodException()
945
946 response = HttpResponse(response_text, JSON_CONTENT_TYPE)
947
948 return response
949
950@safe_rest_view
951def do_ha_failback(request):
952 if request.method != 'PUT':
953 raise RestInvalidMethodException()
954 _result = exec_os_wrapper("HAFailback", 'set', [])
955 response_text = get_successful_response_data('forced failback')
956 response = HttpResponse(response_text, JSON_CONTENT_TYPE)
957 return response
958
959def delete_ha_firewall_rules(ip):
960 rules = FirewallRule.objects.filter(action='allow', src_ip=ip)
961 rules.filter(port=80).delete()
962 rules.filter(proto='tcp', port=7000).delete()
963 rules.filter(proto='vrrp').delete()
964
965def cascade_delete_controller_node(controller_id):
966 ControllerAlias.objects.filter(controller=controller_id).delete()
967 ControllerDomainNameServer.objects.filter(controller=controller_id).delete()
968 for iface in ControllerInterface.objects.filter(controller=controller_id):
969 FirewallRule.objects.filter(interface=iface.id).delete()
970 ControllerInterface.objects.filter(controller=controller_id).delete()
971 Controller.objects.filter(id=controller_id).delete()
972
973# FIXME: this assume a single-interface design and will look for the IP on eth0
974# need to fix this when we have a proper multi-interface design
975def get_controller_node_ip(controller_id):
976 node_ip = ''
977 iface = ControllerInterface.objects.filter(controller=controller_id, type='Ethernet', number=0)
978 if iface:
979 node_ip = iface[0].discovered_ip
980 return node_ip
981
982# This method is "external" facing
983# It is designed to be called by CLI or other REST clients
984# This should only run on the master node, where decommissioning of a remote node is initiated
985@safe_rest_view
986def do_decommission(request):
987 if request.method != 'PUT':
988 raise RestInvalidMethodException()
989 data = simplejson.loads(request.raw_post_data)
990 node_id = data['id']
991
992 # Disallow self-decommissioning
993 local_id = get_local_controller_id()
994 if local_id == node_id:
995 raise RestDecommissionException("Decommissioning of the master node is not allowed. " + \
996 "Please perform a failover first.")
997
998 try :
999 controller = Controller.objects.get(id=node_id)
1000 except Controller.DoesNotExist:
1001 raise RestDecommissionException("No controller found")
1002
1003 node_ip = get_controller_node_ip(node_id)
1004
1005 # Case 1: controller node has IP
1006 if node_ip:
1007 result = exec_os_wrapper("Decommission", 'set', [node_ip])
1008 output = result['out'].strip()
1009 if result['out'].strip().endswith('is already decommissioned'):
1010 delete_ha_firewall_rules(node_ip)
1011 cascade_delete_controller_node(node_id)
1012
1013 # Case 2: controller node has NO IP
1014 else:
1015 output = '%s is already decommissioned' % node_id
1016 cascade_delete_controller_node(node_id)
1017
1018 jsondict = {}
1019 jsondict['status'] = "OK"
1020 jsondict['description'] = output
1021 return HttpResponse(simplejson.dumps(jsondict), JSON_CONTENT_TYPE)
1022
1023# This method is "internal" facing
1024# It is designed to be called only by sys/remove-node.sh
1025# This should only run on the node being decommissioned (slave)
1026@safe_rest_view
1027def do_decommission_internal(request):
1028 if request.method != 'PUT':
1029 raise RestInvalidMethodException()
1030 data = simplejson.loads(request.raw_post_data)
1031 node_ip = data['ip']
1032 exec_os_wrapper("DecommissionLocal", 'set', [node_ip])
1033
1034 jsondict = {}
1035 jsondict['status'] = "OK"
1036 return HttpResponse(simplejson.dumps(jsondict), JSON_CONTENT_TYPE)
1037
1038@safe_rest_view
1039def do_ha_provision(request):
1040 if request.method != 'PUT':
1041 raise RestInvalidMethodException()
1042 data = simplejson.loads(request.raw_post_data)
1043 node_ip = data['ip']
1044
1045 try :
1046 ci = ControllerInterface.objects.get(ip=node_ip)
1047 id = ci.controller.id
1048 print 'got id', id
1049 try:
1050 a = ControllerAlias.objects.get(controller=id)
1051 alias = a.alias
1052 except:
1053 alias = '(no controller alias)'
1054
1055 print 'alias:', alias
1056 raise RestProvisionException('ip address already in controller %s %s' %
1057 (id, alias))
1058
1059 except ControllerInterface.DoesNotExist:
1060 id = uuid.uuid4().urn[9:]
1061 print "generated id = ", id
1062 c = Controller(id=id)
1063 try:
1064 c.save()
1065 except:
1066 # describe failure
1067 raise RestProvisionException('can\t save controller')
1068 pass
1069 print "save controller"
1070 ci = ControllerInterface(controller=c,
1071 ip=node_ip,
1072 discovered_ip=node_ip)
1073 try:
1074 ci.save()
1075 except:
1076 # describe failure
1077 raise RestProvisionException('can\t save controllar interfacer')
1078
1079 for c in Controller.objects.all():
1080 if c.id != id:
1081 #if there are multiple interfaces, assume the
1082 # ethernet0 interface is for management purpose
1083 # XXX this could be better.
1084 iface = ControllerInterface.objects.get(controller=c.id,
1085 type='Ethernet',
1086 number=0)
1087 ip = iface.ip
1088 fw = FirewallRule(interface=iface, action='allow',
1089 src_ip=node_ip, port=80, proto='tcp')
1090 try:
1091 fw.save()
1092 except:
1093 # describe failure
1094 raise RestProvisionException('can\t save firewall rule from master')
1095
1096 fw = FirewallRule(interface=ci, action='allow',
1097 src_ip=ip, port=80, proto='tcp')
1098 try:
1099 fw.save()
1100 except:
1101 raise RestProvisionException('can\t save firewall from slave')
1102
1103
1104 response_text = get_successful_response_data(id)
1105 response = HttpResponse(response_text, JSON_CONTENT_TYPE)
1106
1107 return response
1108
1109
1110def get_clustername():
1111 name = os.popen("grep cluster_name /opt/sdnplatform/db/conf/cassandra.yaml | awk '{print $2}'").readline()
1112 # name may be '', perhaps this ought to None?
1113 return name
1114
1115
1116@safe_rest_view
1117def do_clustername(request):
1118 if request.method != 'GET':
1119 raise RestInvalidMethodException()
1120 response_text = simplejson.dumps([{ 'clustername' : get_clustername() }])
1121 return HttpResponse(response_text, JSON_CONTENT_TYPE)
1122
1123
1124@safe_rest_view
1125def do_local_ha_role(request):
1126 if request.method != 'GET':
1127 raise RestInvalidMethodException()
1128
1129 url = controller_url('core', 'role', 'json')
1130 try:
1131 response_text = urllib2.urlopen(url, timeout=2).read()
1132 response = json.loads(response_text)
1133 except Exception:
1134 response = HttpResponse('{"role":"UNAVAILABLE"}', JSON_CONTENT_TYPE)
1135 return response
1136 # now determine our controller id
1137 controller_id = get_local_controller_id()
1138
1139 # find all the interfaces
1140 ifs = ControllerInterface.objects.filter(controller=controller_id)
1141
1142 for intf in ifs:
1143 firewall_id = '%s|%s|%s' % (controller_id, intf.type, intf.number)
1144
1145 rules = FirewallRule.objects.filter(interface=firewall_id)
1146 for rule in rules:
1147 if rule.action == 'reject' and rule.proto == 'tcp' and rule.port == 6633:
1148 if response['role'] in {'MASTER', 'SLAVE'}:
1149 response['role']+='-BLOCKED'
1150
1151 response['clustername'] = get_clustername()
1152
1153 return HttpResponse(simplejson.dumps(response), JSON_CONTENT_TYPE)
1154
1155@safe_rest_view
1156def do_system_clock(request, local=True):
1157 local_or_utc_str = 'local' if local else 'utc'
1158 if request.method == 'GET':
1159 result = exec_os_wrapper("DateTime", 'get', [local_or_utc_str])
1160 elif request.method == 'PUT':
1161 time_info = simplejson.loads(request.raw_post_data)
1162 dt = datetime(**time_info)
1163 new_date_time_string = dt.strftime('%Y:%m:%d:%H:%M:%S')
1164 result = exec_os_wrapper("DateTime", 'set', [local_or_utc_str, new_date_time_string])
1165 else:
1166 raise RestInvalidMethodException()
1167
1168 if len(result) == 0:
1169 raise Exception('Error executing date command')
1170
1171 # The DateTime OS wrapper only has a single command so the return
1172 # date/time is the first line of the first element of the out array
1173 values = result['out'].strip().split(':')
1174 date_time_info = {
1175 'year': int(values[0]),
1176 'month': int(values[1]),
1177 'day': int(values[2]),
1178 'hour': int(values[3]),
1179 'minute': int(values[4]),
1180 'second': int(values[5]),
1181 'tz': values[6]
1182 }
1183 response_text = simplejson.dumps(date_time_info)
1184 response = HttpResponse(response_text, JSON_CONTENT_TYPE)
1185
1186 return response
1187
1188@safe_rest_view
1189def do_instance(request, model_name,id=None):
1190 """
1191 This function handles both GET and PUT methods.
1192
1193 For a GET request it returns a list of all of the instances of the
1194 model corresponding to the specified type that match the specified
1195 query parameters. If there are no query parameters then it returns
1196 a list of all of the instances of the specified type. The names of
1197 the query parameters can use the Django double underscore syntax
1198 for doing more complicated tests than just equality
1199 (e.g. mac__startswith=192.168).
1200
1201 For a PUT request it can either update an existing instance or
1202 insert one or more new instances. If there are any query parameters
1203 then it assumes that its the update case and that the query
1204 parameters identify exactly one instance. If that's not the case
1205 then an error response is returned. For the update case any subset
1206 of the fields can be updated with the PUT data. The format of the
1207 PUT data is a JSON dictionary
1208 """
1209
1210 # FIXME: Hack to remap 'localhost' id for the controller-node model
1211 # to the real ID for this controller
1212 if model_name == 'controller-node' and id == 'localhost':
1213 id = get_local_controller_id()
1214
1215 # Lookup the model class associated with the specified name
1216 model_info = rest_model_info_dict.get(model_name)
1217 if not model_info:
1218 raise RestInvalidDataTypeException(model_name)
1219
1220 # Set up the keyword argument dictionary we use to filter the QuerySet.
1221 filter_keyword_args = {}
1222
1223 jsonp_prefix = None
1224 nolist = False
1225 order_by = None
1226
1227 # Now iterate over the query params and add further filter keyword arguments
1228 query_param_dict = request.GET
1229 for query_param_name, query_param_value in query_param_dict.items():
1230 add_query_param = False
1231 query_param_name = str(query_param_name)
1232 query_param_value = str(query_param_value)
1233 if query_param_name == 'callback': #switching to match up with jquery getJSON call naming convention.
1234 jsonp_prefix = query_param_value
1235 elif query_param_name == 'nolist':
1236 if query_param_value not in ('False', 'false', '0', ''):
1237 nolist = True
1238 elif query_param_name == 'orderby':
1239 order_by = query_param_value.split(',')
1240 for i in range(len(order_by)):
1241 name = order_by[i]
1242 if name.startswith('-'):
1243 descending = True
1244 name = name[1:]
1245 else:
1246 descending = False
1247 field_info = model_info.rest_name_dict.get(name)
1248 if not field_info:
1249 raise RestInvalidOrderByException(name)
1250 name = field_info.name
1251 if descending:
1252 name = '-' + name
1253 order_by[i] = name
1254 elif query_param_name in model_info.rest_name_dict:
1255 field_info = model_info.rest_name_dict.get(query_param_name)
1256 # For booleans, translate True/False strings into 0/1.
1257 if field_info and type(field_info.django_field_info) == BooleanField:
1258 if query_param_value.lower() == 'false':
1259 query_param_value = 0
1260 elif query_param_value.lower() == 'true':
1261 query_param_value = 1
1262 query_param_name = field_info.name
1263 if model_name == 'controller-node' and \
1264 query_param_name == 'id' and query_param_value == 'localhost':
1265 query_param_value = get_local_controller_id()
1266 if model_name in 'controller-interface' and \
1267 query_param_name == 'controller' and \
1268 query_param_value == 'localhost':
1269 query_param_value = get_local_controller_id()
1270 add_query_param = True
1271 else:
1272 double_underscore_start = query_param_name.find("__")
1273 if double_underscore_start >= 0:
1274 rest_name = query_param_name[:double_underscore_start]
1275 field_info = model_info.rest_name_dict.get(rest_name)
1276 if field_info:
1277 operation = query_param_name[double_underscore_start:]
1278 query_param_name = field_info.name
1279 if type(field_info.django_field_info) == ForeignKey:
1280 query_param_name = query_param_name + '__' + field_info.django_field_info.rel.field_name
1281 # Substitute in the model field name for the (possible renamed) rest name
1282 query_param_name += operation
1283 add_query_param = True
1284 if add_query_param:
1285 filter_keyword_args[query_param_name] = query_param_value
1286
1287 if id != None:
1288 if len(filter_keyword_args) > 0:
1289 raise RestInvalidFilterParameterException(filter_keyword_args.keys()[0])
1290 try:
1291 get_args = {model_info.primary_key:id}
1292 instance = model_info.model_class.objects.get(**get_args)
1293 instance_list = (instance,)
1294 nolist = True
1295 except model_info.model_class.DoesNotExist,e:
1296 raise RestResourceNotFoundException(request.path)
1297 except model_info.model_class.MultipleObjectsReturned, exc:
1298 # traceback.print_exc()
1299 raise RestInternalException(exc)
1300 elif (request.method != 'PUT') or (len(filter_keyword_args) > 0):
1301 # Get the QuerySet based on the keyword arguments we constructed
1302 instance_list = model_info.model_class.objects.filter(**filter_keyword_args)
1303 if order_by:
1304 instance_list = instance_list.order_by(*order_by)
1305 else:
1306 # We're inserting new objects, so there's no need to do a query
1307 instance_list = None
1308
1309 response_content_type = JSON_CONTENT_TYPE
1310
1311 if request.method == 'GET':
1312 json_response_data = []
1313 for instance in instance_list:
1314 json_instance = {}
1315 for field_info in model_info.field_name_dict.values():
1316 # Made some minor edits to deal with a) fields that are empty and b) fields that are not strings -Kyle
1317 # Changed this to only do an explicit string conversion if it's a unicode string.
1318 # The controller is expecting to get the unstringified value (e.g. for boolean values)
1319 # Not sure if this will break things in the UI, but we'll need to resolve how
1320 # we want to handle this. Also, how do we want to handle unicode strings? -- robv
1321 field_name = field_info.name
1322 if type(field_info.django_field_info) == ForeignKey:
1323 field_name += '_id'
1324 value = instance.__dict__.get(field_name)
1325 if value != None:
1326 if field_info.json_serialize:
1327 value = str(value)
1328 json_instance[field_info.rest_name] = value
1329 json_response_data.append(json_instance)
1330
1331 # If the nolist query param was enabled then check to make sure
1332 # that there was only a single instance in the response list and,
1333 # if so, unpack it from the list
1334 if nolist:
1335 if len(json_response_data) != 1:
1336 raise RestNoListResultException()
1337 json_response_data = json_response_data[0]
1338
1339 # Convert to json
1340 response_data = simplejson.dumps(json_response_data)
1341
1342 # If the jsonp query parameter was specified, wrap the data with
1343 # the jsonp prefix
1344 if jsonp_prefix:
1345 response_data = jsonp_prefix + '(' + response_data + ')'
1346 # We don't really know what the content type is here, but it's typically javascript
1347 response_content_type = TEXT_JAVASCRIPT_CONTENT_TYPE
1348 elif request.method == 'PUT':
1349 response_data = get_successful_response_data('saved')
1350 response_content_type = JSON_CONTENT_TYPE
1351
1352 begin_batch_notification()
1353 json_object = simplejson.loads(request.raw_post_data)
1354 if instance_list is not None:
1355
1356 # don't allow the ip address of the first interface to
1357 # be updated once it is set. This really applies to
1358 # the interface cassandra uses to sync the db.
1359 if model_name == 'controller-interface':
1360 for instance in instance_list:
1361 if instance.number == 0 and instance.ip != '':
1362 if 'ip' in json_object and json_object['ip'] != instance.ip:
1363 raise RestModelException("In this version, ip-address of primary interface can't be updated after initial configuration")
1364
1365 # In this case the URL includes query parameter(s) which we assume
1366 # narrow the request to the instances of the model to be updated
1367 # updated with the PUT data. So we're updating existing instances
1368
1369 # If it's a list with one element then extract the single element
1370 if (type(json_object) == list) and (len(json_object) == 1):
1371 json_object = json_object[0]
1372
1373 # We're expecting a dictionary where the keys match the model field names
1374 # If the data isn't a dictionary then return an error
1375 if type(json_object) != dict:
1376 raise RestInvalidPutDataException() # TODO: Should return something different here
1377
1378 # Set the fields in the model instance with the data from the dictionary
1379 for instance in instance_list:
1380 for rest_name, value in json_object.items():
1381 if not rest_name in model_info.rest_name_dict:
1382 raise RestModelException("Model '%s' has no field '%s'" %
1383 (model_name, rest_name))
1384 field_info = model_info.rest_name_dict[rest_name]
1385 field_name = str(field_info.name) # FIXME: Do we need the str cast?
1386 if type(field_info.django_field_info) == ForeignKey:
1387 field_name += '_id'
1388 # TODO: Does Django still not like unicode strings here?
1389 if type(value) == unicode:
1390 value = str(value)
1391 instance.__dict__[field_name] = value
1392 # Save the updated model instance
1393 try:
1394 instance.full_clean()
1395 instance.save()
1396 except ValidationError, err:
1397 handle_validation_error(model_info, err)
1398 #raise RestValidationException(err)
1399 except Exception, exc:
1400 raise RestSaveException(exc)
1401 else:
1402 # In this case no query parameters or id were specified so we're inserting new
1403 # instances into the database. The PUT data can be either a list of new
1404 # items to add (i.e. top level json object is a list) or else a single
1405 # new element (i.e. top-level json object is a dict).
1406 #print "creating object(s)"
1407
1408 # To simplify the logic below we turn the single object case into a list
1409 if type(json_object) != list:
1410 json_object = [json_object]
1411
1412 # Create new model instances for all of the items in the list
1413 for instance_data_dict in json_object:
1414 # We expect the data to be a dictionary keyed by the field names
1415 # in the model. If it's not a dict return an error
1416 if type(instance_data_dict) != dict:
1417 raise RestInvalidPutDataException()
1418
1419 converted_dict = {}
1420
1421 # Now add the fields specified in the PUT data
1422 for rest_name, value in instance_data_dict.items():
1423
1424 #print " processing " + str(name) + " " + str(value)
1425
1426 if not rest_name in model_info.rest_name_dict:
1427 raise RestModelException("Model '%s' has no field '%s'" %
1428 (model_name, rest_name))
1429 field_info = model_info.rest_name_dict[rest_name]
1430 # simplejson uses unicode strings when it loads the objects which
1431 # Django doesn't like that, so we convert these to ASCII strings
1432 if type(rest_name) == unicode:
1433 rest_name = str(rest_name)
1434 if type(value) == unicode:
1435 value = str(value)
1436 field_name = field_info.name
1437 # FIXME: Hack to remap localhost controller node id alias to the actual
1438 # ID for the controller node. We shouldn't be doing this here (this code
1439 # shouldn't have anything about specific models), but it's the easiest
1440 # way to handle it for now and this code is likely going away sometime
1441 # pretty soon (written in May, 2012, let's see how long "pretty soon"
1442 # is :-) )
1443 if model_name == 'controller-node' and field_name == 'id' and value == 'localhost':
1444 value = get_local_controller_id()
1445 if type(field_info.django_field_info) == ForeignKey:
1446 field_name += '_id'
1447 converted_dict[field_name] = value
1448
1449 try:
1450 instance = model_info.model_class(**converted_dict)
1451 instance.full_clean()
1452 instance.save()
1453 except ValidationError, err:
1454 handle_validation_error(model_info, err)
1455 #raise RestValidationException(err)
1456 except Exception, e:
1457 # traceback.print_exc()
1458 raise RestSaveException(e)
1459
1460 end_batch_notification()
1461 elif request.method == 'DELETE':
1462 begin_batch_notification()
1463 for instance in instance_list:
1464 try:
1465 instance.delete()
1466 except ValidationError, err:
1467 handle_validation_error(model_info, err)
1468 except Exception, e:
1469 raise RestException(e)
1470 end_batch_notification()
1471 response_data = "deleted"
1472 response_content_type = 'text/plain'
1473 else:
1474 raise RestInvalidMethodException()
1475
1476 return HttpResponse(response_data, response_content_type)
1477
1478def synthetic_controller_interface(model_name, query_param_dict, json_response_data):
1479 # ---
1480 if model_name == 'controller-interface':
1481 # For controller-interfaces, when an ip address (netmask too)
1482 # is left unconfigured, then it may be possible to associate
1483 # ifconfig details with the interface.
1484 #
1485 # Since controller-interfaces has no mechanism to associate
1486 # specific ifconfig interfaces with rows, it's only possible to
1487 # associate ip's when a single unconfigured ip address exists,
1488 # using a process of elimination. For all ip address in the
1489 # ifconfig output, all statically configured controller-interface
1490 # items are removed. If only one result is left, and only
1491 # one controller-interface has an unconfigured ip address
1492 # (either a dhcp acquired address, or a static address where
1493 # the ip address is uncofigured), the ifconfig ip address
1494 # is very-likely to be the one associated with the
1495 # controller-interface row.
1496
1497 # Check the list of values to see if any are configured as dhcp
1498 dhcp_count = 0
1499 unconfigured_static_ip = 0
1500 this_host = get_local_controller_id()
1501
1502 for entry in json_response_data:
1503 if 'mode' in entry and entry['mode'] == 'dhcp' and \
1504 'controller' in entry and entry['controller'] == this_host:
1505 dhcp_count += 1
1506 if 'mode' in entry and entry['mode'] == 'static' and \
1507 'ip' in entry and entry['ip'] == '':
1508 unconfigured_static_ip += 1
1509 if dhcp_count + unconfigured_static_ip != 1:
1510 for entry in json_response_data:
1511 entry['found-ip'] = entry['ip']
1512 return
1513
1514 need_controller_query = False
1515 # determine whether the complete list of interfaces needs
1516 # to be collected to associate the dhcp address.
1517 for query_param_name, query_param_value in query_param_dict.items():
1518 if query_param_name != 'controller':
1519 need_controller_query = True
1520 if query_param_name == 'controller' and \
1521 query_param_value != this_host:
1522 need_controller_query = True
1523
1524 if need_controller_query == False:
1525 model_interfaces = [x for x in json_response_data
1526 if 'controller' in x and x['controller'] == this_host]
1527 else:
1528 # print 'need to collect all interfaces'
1529 filter_keyword_args = {'controller' : this_host}
1530 model_info = rest_model_info_dict.get(model_name)
1531 instance_list = model_info.model_class.objects.filter(**filter_keyword_args)
1532 response_data = []
1533 for instance in instance_list:
1534 data = {}
1535 for field_info in model_info.field_name_dict.values():
1536 field_name = field_info.name
1537 if type(field_info.django_field_info) == ForeignKey:
1538 field_name += '_id'
1539 value = instance.__dict__.get(field_name)
1540 if value != None:
1541 if field_info.json_serialize:
1542 value = str(value)
1543 data[field_info.rest_name] = value
1544 response_data.append(data)
1545 model_interfaces = response_data
1546
1547 # Recompute the number of dhcp configured interfaces,
1548 # model_interfaces is the collection of interface for 'this_host'
1549 dhcp_count = 0
1550 unconfigured_static_ip = 0
1551 for ifs in model_interfaces:
1552 if 'mode' in ifs and ifs['mode'] == 'dhcp':
1553 dhcp_count += 1
1554 if 'mode' in ifs and ifs['mode'] == 'static' and \
1555 'ip' in ifs and ifs['ip'] == '':
1556 unconfigured_static_ip += 1
1557
1558 if dhcp_count + unconfigured_static_ip != 1:
1559 # print "Sorry, %s dhcp + %s unconfigured static interfaces on %s" % \
1560 # (dhcp_count, unconfigured_static_ip, this_host)
1561 # copy over static ip's
1562 for entry in json_response_data:
1563 entry['found-ip'] = entry['ip']
1564 return
1565
1566 # collect current details for all the network interfaces
1567 inet4_ifs = _collect_system_interfaces()
1568
1569 # iterate over the model_interfaces's interfaces, and
1570 # remove ip addresses from inet4_ifs which are static, and
1571 # have the correct static value.
1572
1573 report_static = False
1574 match_id = ''
1575
1576 for ifs in model_interfaces:
1577 if 'mode' in ifs and ifs['mode'] == 'static':
1578 if 'ip' in ifs and ifs['ip'] == '':
1579 # print "Unconfigured static ip for %s", ifs['id']
1580 match_id = ifs['id']
1581 if 'ip' in ifs and ifs['ip'] != '':
1582 # find this address in the known addresses
1583 remove_entry = -1
1584 for index, inet4_if in enumerate(inet4_ifs):
1585 if inet4_if['addr'] == ifs['ip']:
1586 remove_entry = index
1587 break
1588 if remove_entry == -1:
1589 # print "Static ip %s not found" % ifs['ip']
1590 pass
1591 else:
1592 del inet4_ifs[remove_entry]
1593 elif 'mode' in ifs and ifs['mode'] == 'dhcp':
1594 match_id = ifs['id']
1595 else:
1596 # ought to assert here, not_reached()
1597 pass
1598
1599 # When only one entry is left in inet, its possible to do the assocation
1600 if len(inet4_ifs) != 1:
1601 # print "Incorrect number %s of inet4 interfaces left" % len(inet4_ifs)
1602 pass
1603
1604 for entry in json_response_data:
1605 entry['found-ip'] = entry['ip']
1606 entry['found-netmask'] = entry['netmask']
1607
1608 if entry['id'] == match_id:
1609 # make sure the address isn't set
1610 if entry['ip'] == '':
1611 entry['found-ip'] = inet4_ifs[0]['addr']
1612 entry['found-netmask'] = inet4_ifs[0]['netmask']
1613 entry['found-broadcast'] = inet4_ifs[0]['broadcast']
1614 else:
1615 # ought to assert here, not_reached()
1616 pass
1617
1618@safe_rest_view
1619def do_synthetic_instance(request, model_name, id=None):
1620
1621 if request.method != 'GET':
1622 raise RestInvalidMethodException()
1623
1624 # Lookup the model class associated with the specified name
1625 model_info = rest_model_info_dict.get(model_name)
1626 if not model_info:
1627 raise RestInvalidDataTypeException(model_name)
1628
1629 # Set up the keyword argument dictionary we use to filter the QuerySet.
1630 filter_keyword_args = {}
1631
1632 jsonp_prefix = None
1633 nolist = False
1634 order_by = None
1635
1636 # Now iterate over the query params and add further filter keyword arguments
1637 query_param_dict = request.GET
1638 for query_param_name, query_param_value in query_param_dict.items():
1639 add_query_param = False
1640 query_param_name = str(query_param_name)
1641 query_param_value = str(query_param_value)
1642 if query_param_name == 'callback': #switching to match up with jquery getJSON call naming convention.
1643 jsonp_prefix = query_param_value
1644 elif query_param_name == 'nolist':
1645 if query_param_value not in ('False', 'false', '0', ''):
1646 nolist = True
1647 elif query_param_name == 'orderby':
1648 order_by = query_param_value.split(',')
1649 for i in range(len(order_by)):
1650 name = order_by[i]
1651 if name.startswith('-'):
1652 descending = True
1653 name = name[1:]
1654 else:
1655 descending = False
1656 field_info = model_info.rest_name_dict.get(name)
1657 if not field_info:
1658 raise RestInvalidOrderByException(name)
1659 name = field_info.name
1660 if descending:
1661 name = '-' + name
1662 order_by[i] = name
1663 elif query_param_name in model_info.rest_name_dict:
1664 field_info = model_info.rest_name_dict.get(query_param_name)
1665 query_param_name = field_info.name
1666 add_query_param = True
1667 else:
1668 double_underscore_start = query_param_name.find("__")
1669 if double_underscore_start >= 0:
1670 rest_name = query_param_name[:double_underscore_start]
1671 field_info = model_info.rest_name_dict.get(rest_name)
1672 if field_info:
1673 operation = query_param_name[double_underscore_start:]
1674 query_param_name = field_info.name
1675 if type(field_info.django_field_info) == ForeignKey:
1676 query_param_name = query_param_name + '__' + field_info.django_field_info.rel.field_name
1677 # Substitute in the model field name for the (possible renamed) rest name
1678 query_param_name += operation
1679 add_query_param = True
1680 if add_query_param:
1681 filter_keyword_args[query_param_name] = query_param_value
1682
1683 if id != None:
1684 if len(filter_keyword_args) > 0:
1685 raise RestInvalidFilterParameterException(filter_keyword_args.keys()[0])
1686 try:
1687 get_args = {model_info.primary_key:id}
1688 instance = model_info.model_class.objects.get(**get_args)
1689 instance_list = (instance,)
1690 nolist = True
1691 except model_info.model_class.DoesNotExist,e:
1692 raise RestResourceNotFoundException(request.path)
1693 except model_info.model_class.MultipleObjectsReturned, exc:
1694 # traceback.print_exc()
1695 raise RestInternalException(exc)
1696 elif (request.method != 'PUT') or (len(filter_keyword_args) > 0):
1697 # Get the QuerySet based on the keyword arguments we constructed
1698 instance_list = model_info.model_class.objects.filter(**filter_keyword_args)
1699 if order_by:
1700 instance_list = instance_list.order_by(*order_by)
1701 else:
1702 # We're inserting new objects, so there's no need to do a query
1703 instance_list = None
1704
1705 response_content_type = JSON_CONTENT_TYPE
1706
1707 # Syntheric types only do requests --
1708 json_response_data = []
1709 for instance in instance_list:
1710 json_instance = {}
1711 for field_info in model_info.field_name_dict.values():
1712 # Made some minor edits to deal with a) fields that are empty and b) fields that are not strings -Kyle
1713 # Changed this to only do an explicit string conversion if it's a unicode string.
1714 # The controller is expecting to get the unstringified value (e.g. for boolean values)
1715 # Not sure if this will break things in the UI, but we'll need to resolve how
1716 # we want to handle this. Also, how do we want to handle unicode strings? -- robv
1717 field_name = field_info.name
1718 if type(field_info.django_field_info) == ForeignKey:
1719 field_name += '_id'
1720 value = instance.__dict__.get(field_name)
1721 if value != None:
1722 if field_info.json_serialize:
1723 value = str(value)
1724 json_instance[field_info.rest_name] = value
1725 json_response_data.append(json_instance)
1726
1727 # ---
1728 if model_name == 'controller-interface':
1729 synthetic_controller_interface(model_name, query_param_dict, json_response_data)
1730
1731 # Convert to json
1732 response_data = simplejson.dumps(json_response_data)
1733
1734 # If the nolist query param was enabled then check to make sure
1735 # that there was only a single instance in the response list and,
1736 # if so, unpack it from the list
1737 if nolist:
1738 if len(json_response_data) != 1:
1739 raise RestNoListResultException()
1740 json_response_data = json_response_data[0]
1741
1742 # If the jsonp query parameter was specified, wrap the data with
1743 # the jsonp prefix
1744 if jsonp_prefix:
1745 response_data = jsonp_prefix + '(' + response_data + ')'
1746 # We don't really know what the content type is here, but it's typically javascript
1747 response_content_type = TEXT_JAVASCRIPT_CONTENT_TYPE
1748
1749 return HttpResponse(response_data, response_content_type)
1750
1751@safe_rest_view
1752def do_user_data_list(request):
1753 # Now iterate over the query params and add any valid filter keyword arguments
1754 filter_keyword_args = {}
1755 for query_param_name, query_param_value in request.GET.items():
1756 query_param_name = str(query_param_name)
1757 double_underscore_start = query_param_name.find("__")
1758 if double_underscore_start >= 0:
1759 attribute_name = query_param_name[:double_underscore_start]
1760 else:
1761 attribute_name = query_param_name
1762
1763 # In the future, if we add support for things like mod_date, creation_date, etc.
1764 # which would be supported in query params, then they'd be added to this list/tuple.
1765 if attribute_name not in ('name',):
1766 raise RestInvalidFilterParameterException(query_param_name)
1767 filter_keyword_args[query_param_name] = query_param_value
1768
1769 instance_list = UserData.objects.filter(**filter_keyword_args)
1770
1771 if request.method == 'GET':
1772 user_data_info_list = []
1773
1774 # FIXME: robv: It's incorrect to *always* add this to the user data,
1775 # because it means we're not respecting the filter query parameters.
1776 # To work completely correctly we'd need to duplicate a lot of logic
1777 # for processing the query parameters, which would be tedious.
1778 # Should talk to Mandeep about why it was done this way. Maybe we
1779 # should expose these special cases in a different URL/view.
1780 for fn in ['startup-config', 'upgrade-config']:
1781 try:
1782 sc = "%s/run/%s" % (sdncon.SDN_ROOT, fn)
1783 f = open(sc, 'r')
1784 f.close()
1785 t = time.strftime("%Y-%m-%d.%H:%M:%S",
1786 time.localtime(os.path.getmtime(sc)))
1787 instance_name = fn + '/timestamp=' + t + \
1788 '/version=1/length=' + \
1789 str(os.path.getsize(sc))
1790 url_path = 'rest/v1/data/' + instance_name + '/'
1791
1792 user_data_info = { 'name' : instance_name,
1793 'url_path' : url_path, }
1794 user_data_info_list.append(user_data_info)
1795 except:
1796 pass
1797
1798 for instance in instance_list:
1799 user_data_info = {'name': instance.name,
1800 'url_path': 'rest/v1/data/' + instance.name + '/'}
1801 user_data_info_list.append(user_data_info)
1802
1803 response_data = simplejson.dumps(user_data_info_list)
1804 elif request.method == 'DELETE':
1805 instance_list.delete()
1806 response_data = {}
1807 response_data['status'] = 'success'
1808 response_data['message'] = 'user data deleted'
1809 response_data = simplejson.dumps(response_data)
1810 response_content_type = JSON_CONTENT_TYPE
1811 else:
1812 raise RestInvalidMethodException()
1813
1814 return HttpResponse(response_data, JSON_CONTENT_TYPE)
1815
1816@safe_rest_view
1817def do_user_data(request, name):
1818 query_param_dict = request.GET
1819 #
1820 # Manage startup-config/update-config differently
1821 if name.find('/') >= 0 and \
1822 name.split('/')[0] in ['startup-config', 'upgrade-config']:
1823 path = "%s/run/%s" % (sdncon.SDN_ROOT, name.split('/')[0])
1824 response_data = {}
1825
1826 if request.method == 'GET':
1827 with open(path, 'r') as f:
1828 response_data = f.read()
1829 response_content_type = "text/plain"
1830 elif request.method == 'PUT':
1831 try:
1832 with open(path, 'w') as f:
1833 f.write(request.raw_post_data)
1834 response_data['status'] = 'success'
1835 response_data['message'] = 'user data updated'
1836 except:
1837 response_data['status'] = 'failure'
1838 response_data['message'] = "can't write file"
1839 response_content_type = JSON_CONTENT_TYPE
1840 response_data = simplejson.dumps(response_data)
1841 elif request.method == 'DELETE':
1842 try:
1843 f = open(path, "r")
1844 f.close()
1845 except:
1846 raise RestResourceNotFoundException(request.path)
1847
1848 try:
1849 os.remove(path)
1850 response_data['status'] = 'success'
1851 response_data['message'] = 'user data deleted'
1852 except:
1853 response_data['status'] = 'failure'
1854 response_data['message'] = "can't delete file"
1855 response_data = simplejson.dumps(response_data)
1856 response_content_type = JSON_CONTENT_TYPE
1857 else:
1858 raise RestInvalidMethodException()
1859
1860 return HttpResponse(response_data, response_content_type)
1861
1862
1863 # Default values for optional query parameters
1864 #private = False
1865 binary = False
1866
1867 for param_name, param_value in query_param_dict.items():
1868 if param_name == 'binary':
1869 if request.method != 'PUT':
1870 raise RestInvalidQueryParameterException(name)
1871 binary = param_value.lower() == 'true' or param_value == '1'
1872 #elif param_name == 'private':
1873 # private = param_value
1874 else:
1875 raise RestInvalidQueryParameterException(param_name)
1876
1877 # FIXME: Need HTTP basic/digest auth support for the following
1878 # code to work.
1879 #if private:
1880 # user = request.user
1881 #else:
1882 # user = None
1883 #if user != None and not user.is_authenticated():
1884 # raise RestAuthenticationRequiredException()
1885 user = None
1886
1887 # There's currently an issue with filtering on the user when using the
1888 # Cassandra database backend. Since we don't support private per-user
1889 # data right now, I'm just disabling filtering on the user and only
1890 # filter on the name
1891 #user_data_query_set = UserData.objects.filter(user=user, name=name)
1892 user_data_query_set = UserData.objects.filter(name=name)
1893
1894 count = user_data_query_set.count()
1895 if count > 1:
1896 raise RestInternalException('Duplicate user data values for the same name')
1897
1898 if request.method == 'GET':
1899 if count == 0:
1900 raise RestResourceNotFoundException(request.path)
1901 user_data = user_data_query_set[0]
1902 response_data = user_data.data
1903 if user_data.binary:
1904 response_data = base64.b64decode(response_data)
1905 response_content_type = user_data.content_type
1906 elif request.method == 'PUT':
1907 content_type = request.META['CONTENT_TYPE']
1908 if content_type == None:
1909 if binary:
1910 content_type = BINARY_DATA_CONTENT_TYPE
1911 else:
1912 content_type = JSON_CONTENT_TYPE
1913 response_data = {}
1914 if count == 1:
1915 response_data['status'] = 'success'
1916 response_data['message'] = 'user data updated'
1917 user_data = user_data_query_set[0]
1918 else:
1919 response_data['status'] = 'success'
1920 response_data['message'] = 'user data created'
1921 user_data = UserData(user=user,name=name)
1922 user_data.binary = binary
1923 user_data.content_type = content_type
1924 data = request.raw_post_data
1925 if binary:
1926 data = base64.b64encode(data)
1927 user_data.data = data
1928 user_data.save()
1929 response_data = simplejson.dumps(response_data)
1930 response_content_type = JSON_CONTENT_TYPE
1931 elif request.method == 'DELETE':
1932 if count == 0:
1933 raise RestResourceNotFoundException(request.path)
1934 user_data = user_data_query_set[0]
1935 user_data.delete()
1936 response_data = {}
1937 response_data['status'] = 'success'
1938 response_data['message'] = 'user data deleted'
1939 response_data = simplejson.dumps(response_data)
1940 response_content_type = JSON_CONTENT_TYPE
1941 else:
1942 raise RestInvalidMethodException()
1943
1944 return HttpResponse(response_data, response_content_type)
1945
1946@safe_rest_view
1947def do_sdnplatform_tunnel_manager(request, dpid=None):
1948 """
1949 This returns realtime statistics from sdnplatform
1950 """
1951
1952 if request.method != 'GET':
1953 raise RestInvalidMethodException()
1954 if dpid == None:
1955 raise RestInvalidMethodException()
1956
1957 print 'DPID', dpid
1958 if dpid == 'all':
1959 url = controller_url('vns', 'tunnel-manager', 'all', 'json')
1960 else:
1961 url = controller_url('vns', 'tunnel-manager', 'switch='+dpid, 'json')
1962
1963 response_text = urllib2.urlopen(url).read()
1964 entries = simplejson.loads(response_text)
1965
1966 if 'error' in entries and entries['error'] != None:
1967 RestInternalException(entries['error'])
1968
1969 return HttpResponse(json.dumps(entries['tunnMap']), JSON_CONTENT_TYPE)
1970
1971@safe_rest_view
1972def do_sdnplatform_controller_summary(request):
1973 """
1974 This returns summary statistics from sdnplatform modules
1975 """
1976 if request.method != 'GET':
1977 raise RestInvalidMethodException()
1978
1979 url = controller_url('core', 'controller', 'summary', 'json')
1980 return get_sdnplatform_response(url)
1981
1982def filter_queries(choice_list, param_dict):
1983 return dict([[x, param_dict[x]] for x in choice_list
1984 if x in param_dict and param_dict[x] != 'all'])
1985
1986@safe_rest_view
1987def do_reload(request):
1988 """
1989 This calls an oswrapper that reloads the box.
1990 """
1991 if request.method != 'GET':
1992 raise RestInvalidMethodException()
1993 exec_os_wrapper("ReloadController", 'set', [])
1994 response_text = '{"status":"reloading"}'
1995 return HttpResponse(response_text, JSON_CONTENT_TYPE)
1996
1997@safe_rest_view
1998def do_resetbsc(request):
1999 """
2000 This calls an oswrapper that resets the box.
2001 """
2002 if request.method != 'PUT':
2003 raise RestInvalidMethodException()
2004 exec_os_wrapper("ResetBsc", 'set', [])
2005 response_text = '{"status":"resetting"}'
2006 return HttpResponse(response_text, JSON_CONTENT_TYPE)
2007
2008@safe_rest_view
2009def do_abort_upgrade(request):
2010 """
2011 This calls an oswrapper that reloads the box.
2012 """
2013 if request.method != 'PUT':
2014 raise RestInvalidMethodException()
2015 controller_id = get_local_controller_id()
2016 controller = Controller.objects.get(id=controller_id)
2017 if controller.status != 'Upgrading':
2018 raise RestUpgradeException("No Upgrade pending")
2019 exec_os_wrapper("AbortUpgrade", 'set', [])
2020 controller.status = 'Ready'
2021 controller.save()
2022 response_text = '{"status":"Ready"}'
2023 return HttpResponse(response_text, JSON_CONTENT_TYPE)
2024
2025@safe_rest_view
2026def do_config_rollback(request):
2027 data = simplejson.loads(request.raw_post_data)
2028 path = data['path']
2029 print "Executing config rollback with config @", path
2030
2031 if request.method != 'PUT':
2032 raise RestInvalidMethodException()
2033 exec_os_wrapper("RollbackConfig", 'set', [path])
2034 response_text = get_successful_response_data('prepared config rollbacked')
2035 return HttpResponse(response_text, JSON_CONTENT_TYPE)
2036
2037@safe_rest_view
2038def do_upload_data(request):
2039 if request.method != 'PUT':
2040 raise RestInvalidMethodException()
2041 data = simplejson.loads(request.raw_post_data)
2042 content = data['data']
2043 path = data['dst']
2044 print "Executing config rollback with config @", path
2045
2046 exec_os_wrapper("WriteDataToFile", 'set', [content, path])
2047 response_text = get_successful_response_data('written data')
2048 return HttpResponse(response_text, JSON_CONTENT_TYPE)
2049
2050@safe_rest_view
2051def do_diff_config(request):
2052 if request.method != 'PUT':
2053 raise RestInvalidMethodException()
2054 data = simplejson.loads(request.raw_post_data)
2055 config1 = data['config-1']
2056 config2 = data['config-2']
2057 print "diffing '%s' with '%s'" %(config1, config2)
2058
2059 result = exec_os_wrapper("DiffConfig", 'set', [config1, config2])
2060 return HttpResponse(simplejson.dumps(result), JSON_CONTENT_TYPE)
2061
2062@safe_rest_view
2063def do_extract_upgrade_pkg_manifest(request):
2064 """
2065 This calls an oswrapper that extracts the upgrade package.
2066 This returns the install package 'manifest'.
2067 """
2068 if request.method != 'GET':
2069 raise RestInvalidMethodException()
2070 exec_os_wrapper("GetLatestUpgradePkg", 'get', [])
2071 output = exec_os_wrapper("CatUpgradeImagesFile", 'get')
2072 upgradePkg = output['out'].strip()
2073 exec_os_wrapper("ExtractUpgradePkgManifest", 'set', [upgradePkg])
2074 output = exec_os_wrapper("ExtractUpgradePkgManifest", 'get')
2075 manifest = output['out']
2076 return HttpResponse(manifest, JSON_CONTENT_TYPE)
2077
2078@safe_rest_view
2079def do_extract_upgrade_pkg(request):
2080 if request.method != 'GET':
2081 raise RestInvalidMethodException()
2082 exec_os_wrapper("GetLatestUpgradePkg", 'get', [])
2083 output = exec_os_wrapper("CatUpgradeImagesFile", 'get')
2084 upgradePkg = output['out'].strip()
2085 exec_os_wrapper("ExtractUpgradePkg", 'set', [upgradePkg])
2086 return HttpResponse('{"status": "OK"}', JSON_CONTENT_TYPE)
2087
2088@safe_rest_view
2089def do_get_upgrade_pkg(request):
2090 """
2091 This calls an oswrapper to get the latest upgrade
2092 package uploaded to the controller.
2093 """
2094 if request.method != 'GET':
2095 raise RestInvalidMethodException()
2096 exec_os_wrapper("GetLatestUpgradePkg", 'get')
2097 result = exec_os_wrapper("CatUpgradeImagesFile", 'get')
2098 jsondict = {'file': result['out'].strip()}
2099 return HttpResponse(simplejson.dumps(jsondict), JSON_CONTENT_TYPE)
2100
2101@safe_rest_view
2102def do_cleanup_old_pkgs(request):
2103 if request.method != 'GET':
2104 raise RestInvalidMethodException()
2105 exec_os_wrapper("CleanupOldUpgradeImages", 'get')
2106 return HttpResponse('{"status": "OK"}', JSON_CONTENT_TYPE)
2107
2108@safe_rest_view
2109def do_execute_upgrade_step(request):
2110 """
2111 Executes a particular upgrade step according to the
2112 upgrade package manifest.
2113 """
2114 if request.method != 'PUT':
2115 raise RestInvalidMethodException()
2116
2117 put_data = json.loads(request.raw_post_data)
2118 imageName = put_data.get('imageName')
2119 stepNum = put_data.get('step')
2120 force = put_data.get('force')
2121
2122 args = [stepNum, imageName]
2123 if force:
2124 args.append("--force")
2125 result = exec_os_wrapper("ExecuteUpgradeStep", 'get',
2126 args)
2127 jsondict = {}
2128 if len(str(result['err']).strip()) > 0:
2129 jsondict['status'] = "ERROR"
2130 jsondict['description'] = str(result['err']).strip()
2131 else:
2132 jsondict['status'] = "OK"
2133 jsondict['description'] = str(result['out']).strip()
2134
2135 return HttpResponse(simplejson.dumps(jsondict), JSON_CONTENT_TYPE)
Srikanth Vavilapalli7ff259b2014-10-17 16:19:16 -07002136
2137@safe_rest_view
2138def do_sdnplatform_tunnel_config(request):
Srikanth Vavilapallifc6891c2014-10-19 20:46:17 -07002139 if request.method != 'PUT' and request.method != 'DELETE':
Srikanth Vavilapalli7ff259b2014-10-17 16:19:16 -07002140 raise RestInvalidMethodException()
2141
2142 url = controller_url('onos', 'segmentrouting', 'tunnel')
2143 post_data = request.raw_post_data
2144 put_request = urllib2.Request(url, post_data)
Srikanth Vavilapallifc6891c2014-10-19 20:46:17 -07002145 method = request.method
2146 if method == 'PUT':
2147 method = 'POST'
2148 put_request.get_method = lambda: method
2149 put_request.add_header('Content-Type', 'application/json')
2150 response = urllib2.urlopen(put_request)
2151 response_text = response.read()
2152 response = HttpResponse(response_text, JSON_CONTENT_TYPE)
2153
2154 return response
2155
2156@safe_rest_view
2157def do_sdnplatform_policy_config(request):
2158 if request.method != 'PUT' and request.method != 'DELETE':
2159 raise RestInvalidMethodException()
2160
2161 url = controller_url('onos', 'segmentrouting', 'policy')
2162 post_data = request.raw_post_data
2163 put_request = urllib2.Request(url, post_data)
2164 method = request.method
2165 if method == 'PUT':
2166 method = 'POST'
2167 put_request.get_method = lambda: method
Srikanth Vavilapalli7ff259b2014-10-17 16:19:16 -07002168 put_request.add_header('Content-Type', 'application/json')
2169 response = urllib2.urlopen(put_request)
2170 response_text = response.read()
2171 response = HttpResponse(response_text, JSON_CONTENT_TYPE)
srikanth116e6e82014-08-19 07:22:37 -07002172
Srikanth Vavilapalli7ff259b2014-10-17 16:19:16 -07002173 return response
Fahad Naeem Khan44a973b2014-10-20 13:59:55 -07002174
2175@safe_rest_view
2176def do_show_tunnel(request):
2177 #if request.method != 'GET':
2178 # raise RestInvalidMethodException()
2179
2180 url = controller_url('onos', 'segmentrouting','tunnel')
2181 if request.META['QUERY_STRING']:
2182 url += '?' + request.META['QUERY_STRING']
2183 return get_sdnplatform_response(url)
2184
2185@safe_rest_view
2186def do_show_policy(request):
2187 #if request.method != 'GET':
2188 # raise RestInvalidMethodException()
2189
2190 url = controller_url('onos', 'segmentrouting','policy')
2191 if request.META['QUERY_STRING']:
2192 url += '?' + request.META['QUERY_STRING']
2193 return get_sdnplatform_response(url)