blob: f8d15a22bf532a6f64a640c62f43909703cf28de [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 Khan1f8fb2e2014-09-25 16:39:18 -0700735def do_mastership(request):
736 url = controller_url("onos", "registry", "switches" ,"json")
737 #url = "http://127.0.0.1:8080/wm/onos/registry/switches/json"
738 if request.META['QUERY_STRING']:
739 url += '?' + request.META['QUERY_STRING']
Fahad Naeem Khan99ce8062014-10-01 11:03:54 -0700740 return get_sdnplatform_response(url)
741
742@safe_rest_view
743def do_controller(request):
744 url = controller_url("onos", "registry", "controllers" ,"json")
745 #url = "http://127.0.0.1:8080/wm/onos/registry/switches/json"
746 if request.META['QUERY_STRING']:
747 url += '?' + request.META['QUERY_STRING']
Fahad Naeem Khan1f8fb2e2014-09-25 16:39:18 -0700748 return get_sdnplatform_response(url)
749#'''
750
751@safe_rest_view
srikanth116e6e82014-08-19 07:22:37 -0700752def do_links(request):
srikanthde50b092014-08-19 08:31:46 -0700753 if onos == 0:
754 url = controller_url("topology", "links", "json")
755 else:
756 url = controller_url("onos", "topology", "links")
srikanth116e6e82014-08-19 07:22:37 -0700757 if request.META['QUERY_STRING']:
758 url += '?' + request.META['QUERY_STRING']
759 return get_sdnplatform_response(url)
760
761@safe_rest_view
762def do_vns_device_interface(request):
763 return get_sdnplatform_query(request, "vns/device-interface")
764
765@safe_rest_view
766def do_vns_interface(request):
767 return get_sdnplatform_query(request, "vns/interface")
768
769@safe_rest_view
770def do_vns(request):
771 return get_sdnplatform_query(request, "vns")
772
773@safe_rest_view
774def do_system_version(request):
775 if request.method != 'GET':
776 raise RestInvalidMethodException()
777 version = get_system_version_string()
778 response_text = simplejson.dumps([{ 'controller' : version }])
779 return HttpResponse(response_text, JSON_CONTENT_TYPE)
780
781available_log_files = {
782 'syslog' : '/var/log/syslog',
783 'sdnplatform' : '/opt/sdnplatform/sdnplatform/log/sdnplatform.log',
784 'console-access' : '/opt/sdnplatform/con/log/access.log',
785 'cassandra' : '/opt/sdnplatform/db/log/system.log',
786 'authlog' : '/var/log/auth.log',
787 'pre-start' : '/tmp/pre-start',
788 'post-start' : '/tmp/post-start',
789 # 'ftp' : '/var/log/ftp.log',
790}
791
792available_log_commands = {
793 'dmesg' : 'dmesg',
794 'process' : 'ps lax'
795}
796
797@safe_rest_view
798def do_system_log_list(request):
799 if request.method != 'GET':
800 raise RestInvalidMethodException()
801 existing_logs = []
802 for (log_name, log_path) in available_log_files.items():
803 try:
804 log_file = open(log_path, 'r')
805 existing_logs.append({ 'log' : log_name })
806 log_file.close()
807 except Exception, e:
808 pass
809
810 print '??'
811 for log_name in available_log_commands.keys():
812 print 'ADD', log_name
813 existing_logs.append({ 'log' : log_name })
814 response_text = simplejson.dumps(existing_logs)
815 return HttpResponse(response_text, JSON_CONTENT_TYPE)
816
817
818def generate_subprocess_output(cmd):
819
820 process = subprocess.Popen(cmd, shell=True,
821 stdout=subprocess.PIPE,
822 stderr=subprocess.STDOUT,
823 bufsize=1)
824 while True:
825 line = process.stdout.readline()
826 if line != None and line != "":
827 yield line
828 else:
829 break
830
831
832@safe_rest_view
833def do_system_log(request, log_name):
834 if request.method != 'GET':
835 raise RestInvalidMethodException()
836 print 'do system log', log_name
837
838 # manage command ouput differently
839 if log_name in available_log_commands:
840 cmd = available_log_commands[log_name]
841 print 'DOING COMMAND', cmd
842
843 return HttpResponse(generate_subprocess_output(cmd),
844 TEXT_PLAIN_CONTENT_TYPE)
845 return
846
847 log_path = available_log_files.get(log_name)
848 if log_name == None:
849 raise RestInvalidLog('No such log: %s' % log_name)
850
851 try:
852 log_file = open(log_path, 'r')
853 except Exception,e:
854 raise RestInvalidLog('Log does not exist: %s' % log_name)
855
856 # use a generator so that the complete log is not ever held in memory
857 def response(log_name, file):
858 for line in file:
859 yield line
860 file.close()
861
862 return HttpResponse(response(log_name, log_file), TEXT_PLAIN_CONTENT_TYPE)
863
864
865@safe_rest_view
866def do_system_uptime(request):
867 if request.method != 'GET':
868 raise RestInvalidMethodException()
869 url = controller_url('core', 'system', 'uptime', 'json')
870 return get_sdnplatform_response(url)
871
872
873def _collect_system_interfaces(lo = False):
874 from netifaces import interfaces, ifaddresses, AF_INET, AF_LINK
875 result = []
876 for iface in interfaces():
877 if iface.startswith('lo') and not lo:
878 continue # ignore loopback
879 addrs = ifaddresses(iface)
880 if AF_INET in addrs:
881 for addr in ifaddresses(iface)[AF_INET]:
882 result.append({'name' : iface,
883 'addr' : addr.get('addr', ''),
884 'netmask' : addr.get('netmask', ''),
885 'broadcast' : addr.get('broadcast', ''),
886 'peer' : addr.get('peer', '')})
887 return result
888
889
890def do_system_inet4_interfaces(request):
891 if request.method != 'GET':
892 raise RestInvalidMethodException()
893 response_text = simplejson.dumps(_collect_system_interfaces(lo = True))
894 return HttpResponse(response_text, JSON_CONTENT_TYPE)
895
896
897@safe_rest_view
898def do_system_time_zone_strings(request, list_type):
899 import pytz
900 if list_type == 'common':
901 string_list = pytz.common_timezones
902 elif list_type == "all":
903 string_list = pytz.all_timezones
904 else:
905 raise RestResourceNotFoundException(request.path)
906
907 response_text = simplejson.dumps(string_list)
908
909 return HttpResponse(response_text, JSON_CONTENT_TYPE)
910
911@safe_rest_view
912def do_check_config(request):
913 config_check_state()
914 return get_successful_response('checked config')
915
916@safe_rest_view
917def do_local_controller_id(request):
918 if request.method == 'GET':
919 controller_id = get_local_controller_id()
920 if not controller_id:
921 raise Exception("Unspecified local controller id")
922 response_text = simplejson.dumps({'id': controller_id})
923 elif request.method == 'PUT':
924 put_data = json.loads(request.raw_post_data)
925 controller_id = put_data.get('id')
926 _result = exec_os_wrapper("ControllerId", 'set', [controller_id])
927 response_text = get_successful_response_data('updated')
928 else:
929 raise RestInvalidMethodException()
930
931 response = HttpResponse(response_text, JSON_CONTENT_TYPE)
932
933 return response
934
935@safe_rest_view
936def do_ha_failback(request):
937 if request.method != 'PUT':
938 raise RestInvalidMethodException()
939 _result = exec_os_wrapper("HAFailback", 'set', [])
940 response_text = get_successful_response_data('forced failback')
941 response = HttpResponse(response_text, JSON_CONTENT_TYPE)
942 return response
943
944def delete_ha_firewall_rules(ip):
945 rules = FirewallRule.objects.filter(action='allow', src_ip=ip)
946 rules.filter(port=80).delete()
947 rules.filter(proto='tcp', port=7000).delete()
948 rules.filter(proto='vrrp').delete()
949
950def cascade_delete_controller_node(controller_id):
951 ControllerAlias.objects.filter(controller=controller_id).delete()
952 ControllerDomainNameServer.objects.filter(controller=controller_id).delete()
953 for iface in ControllerInterface.objects.filter(controller=controller_id):
954 FirewallRule.objects.filter(interface=iface.id).delete()
955 ControllerInterface.objects.filter(controller=controller_id).delete()
956 Controller.objects.filter(id=controller_id).delete()
957
958# FIXME: this assume a single-interface design and will look for the IP on eth0
959# need to fix this when we have a proper multi-interface design
960def get_controller_node_ip(controller_id):
961 node_ip = ''
962 iface = ControllerInterface.objects.filter(controller=controller_id, type='Ethernet', number=0)
963 if iface:
964 node_ip = iface[0].discovered_ip
965 return node_ip
966
967# This method is "external" facing
968# It is designed to be called by CLI or other REST clients
969# This should only run on the master node, where decommissioning of a remote node is initiated
970@safe_rest_view
971def do_decommission(request):
972 if request.method != 'PUT':
973 raise RestInvalidMethodException()
974 data = simplejson.loads(request.raw_post_data)
975 node_id = data['id']
976
977 # Disallow self-decommissioning
978 local_id = get_local_controller_id()
979 if local_id == node_id:
980 raise RestDecommissionException("Decommissioning of the master node is not allowed. " + \
981 "Please perform a failover first.")
982
983 try :
984 controller = Controller.objects.get(id=node_id)
985 except Controller.DoesNotExist:
986 raise RestDecommissionException("No controller found")
987
988 node_ip = get_controller_node_ip(node_id)
989
990 # Case 1: controller node has IP
991 if node_ip:
992 result = exec_os_wrapper("Decommission", 'set', [node_ip])
993 output = result['out'].strip()
994 if result['out'].strip().endswith('is already decommissioned'):
995 delete_ha_firewall_rules(node_ip)
996 cascade_delete_controller_node(node_id)
997
998 # Case 2: controller node has NO IP
999 else:
1000 output = '%s is already decommissioned' % node_id
1001 cascade_delete_controller_node(node_id)
1002
1003 jsondict = {}
1004 jsondict['status'] = "OK"
1005 jsondict['description'] = output
1006 return HttpResponse(simplejson.dumps(jsondict), JSON_CONTENT_TYPE)
1007
1008# This method is "internal" facing
1009# It is designed to be called only by sys/remove-node.sh
1010# This should only run on the node being decommissioned (slave)
1011@safe_rest_view
1012def do_decommission_internal(request):
1013 if request.method != 'PUT':
1014 raise RestInvalidMethodException()
1015 data = simplejson.loads(request.raw_post_data)
1016 node_ip = data['ip']
1017 exec_os_wrapper("DecommissionLocal", 'set', [node_ip])
1018
1019 jsondict = {}
1020 jsondict['status'] = "OK"
1021 return HttpResponse(simplejson.dumps(jsondict), JSON_CONTENT_TYPE)
1022
1023@safe_rest_view
1024def do_ha_provision(request):
1025 if request.method != 'PUT':
1026 raise RestInvalidMethodException()
1027 data = simplejson.loads(request.raw_post_data)
1028 node_ip = data['ip']
1029
1030 try :
1031 ci = ControllerInterface.objects.get(ip=node_ip)
1032 id = ci.controller.id
1033 print 'got id', id
1034 try:
1035 a = ControllerAlias.objects.get(controller=id)
1036 alias = a.alias
1037 except:
1038 alias = '(no controller alias)'
1039
1040 print 'alias:', alias
1041 raise RestProvisionException('ip address already in controller %s %s' %
1042 (id, alias))
1043
1044 except ControllerInterface.DoesNotExist:
1045 id = uuid.uuid4().urn[9:]
1046 print "generated id = ", id
1047 c = Controller(id=id)
1048 try:
1049 c.save()
1050 except:
1051 # describe failure
1052 raise RestProvisionException('can\t save controller')
1053 pass
1054 print "save controller"
1055 ci = ControllerInterface(controller=c,
1056 ip=node_ip,
1057 discovered_ip=node_ip)
1058 try:
1059 ci.save()
1060 except:
1061 # describe failure
1062 raise RestProvisionException('can\t save controllar interfacer')
1063
1064 for c in Controller.objects.all():
1065 if c.id != id:
1066 #if there are multiple interfaces, assume the
1067 # ethernet0 interface is for management purpose
1068 # XXX this could be better.
1069 iface = ControllerInterface.objects.get(controller=c.id,
1070 type='Ethernet',
1071 number=0)
1072 ip = iface.ip
1073 fw = FirewallRule(interface=iface, action='allow',
1074 src_ip=node_ip, port=80, proto='tcp')
1075 try:
1076 fw.save()
1077 except:
1078 # describe failure
1079 raise RestProvisionException('can\t save firewall rule from master')
1080
1081 fw = FirewallRule(interface=ci, action='allow',
1082 src_ip=ip, port=80, proto='tcp')
1083 try:
1084 fw.save()
1085 except:
1086 raise RestProvisionException('can\t save firewall from slave')
1087
1088
1089 response_text = get_successful_response_data(id)
1090 response = HttpResponse(response_text, JSON_CONTENT_TYPE)
1091
1092 return response
1093
1094
1095def get_clustername():
1096 name = os.popen("grep cluster_name /opt/sdnplatform/db/conf/cassandra.yaml | awk '{print $2}'").readline()
1097 # name may be '', perhaps this ought to None?
1098 return name
1099
1100
1101@safe_rest_view
1102def do_clustername(request):
1103 if request.method != 'GET':
1104 raise RestInvalidMethodException()
1105 response_text = simplejson.dumps([{ 'clustername' : get_clustername() }])
1106 return HttpResponse(response_text, JSON_CONTENT_TYPE)
1107
1108
1109@safe_rest_view
1110def do_local_ha_role(request):
1111 if request.method != 'GET':
1112 raise RestInvalidMethodException()
1113
1114 url = controller_url('core', 'role', 'json')
1115 try:
1116 response_text = urllib2.urlopen(url, timeout=2).read()
1117 response = json.loads(response_text)
1118 except Exception:
1119 response = HttpResponse('{"role":"UNAVAILABLE"}', JSON_CONTENT_TYPE)
1120 return response
1121 # now determine our controller id
1122 controller_id = get_local_controller_id()
1123
1124 # find all the interfaces
1125 ifs = ControllerInterface.objects.filter(controller=controller_id)
1126
1127 for intf in ifs:
1128 firewall_id = '%s|%s|%s' % (controller_id, intf.type, intf.number)
1129
1130 rules = FirewallRule.objects.filter(interface=firewall_id)
1131 for rule in rules:
1132 if rule.action == 'reject' and rule.proto == 'tcp' and rule.port == 6633:
1133 if response['role'] in {'MASTER', 'SLAVE'}:
1134 response['role']+='-BLOCKED'
1135
1136 response['clustername'] = get_clustername()
1137
1138 return HttpResponse(simplejson.dumps(response), JSON_CONTENT_TYPE)
1139
1140@safe_rest_view
1141def do_system_clock(request, local=True):
1142 local_or_utc_str = 'local' if local else 'utc'
1143 if request.method == 'GET':
1144 result = exec_os_wrapper("DateTime", 'get', [local_or_utc_str])
1145 elif request.method == 'PUT':
1146 time_info = simplejson.loads(request.raw_post_data)
1147 dt = datetime(**time_info)
1148 new_date_time_string = dt.strftime('%Y:%m:%d:%H:%M:%S')
1149 result = exec_os_wrapper("DateTime", 'set', [local_or_utc_str, new_date_time_string])
1150 else:
1151 raise RestInvalidMethodException()
1152
1153 if len(result) == 0:
1154 raise Exception('Error executing date command')
1155
1156 # The DateTime OS wrapper only has a single command so the return
1157 # date/time is the first line of the first element of the out array
1158 values = result['out'].strip().split(':')
1159 date_time_info = {
1160 'year': int(values[0]),
1161 'month': int(values[1]),
1162 'day': int(values[2]),
1163 'hour': int(values[3]),
1164 'minute': int(values[4]),
1165 'second': int(values[5]),
1166 'tz': values[6]
1167 }
1168 response_text = simplejson.dumps(date_time_info)
1169 response = HttpResponse(response_text, JSON_CONTENT_TYPE)
1170
1171 return response
1172
1173@safe_rest_view
1174def do_instance(request, model_name,id=None):
1175 """
1176 This function handles both GET and PUT methods.
1177
1178 For a GET request it returns a list of all of the instances of the
1179 model corresponding to the specified type that match the specified
1180 query parameters. If there are no query parameters then it returns
1181 a list of all of the instances of the specified type. The names of
1182 the query parameters can use the Django double underscore syntax
1183 for doing more complicated tests than just equality
1184 (e.g. mac__startswith=192.168).
1185
1186 For a PUT request it can either update an existing instance or
1187 insert one or more new instances. If there are any query parameters
1188 then it assumes that its the update case and that the query
1189 parameters identify exactly one instance. If that's not the case
1190 then an error response is returned. For the update case any subset
1191 of the fields can be updated with the PUT data. The format of the
1192 PUT data is a JSON dictionary
1193 """
1194
1195 # FIXME: Hack to remap 'localhost' id for the controller-node model
1196 # to the real ID for this controller
1197 if model_name == 'controller-node' and id == 'localhost':
1198 id = get_local_controller_id()
1199
1200 # Lookup the model class associated with the specified name
1201 model_info = rest_model_info_dict.get(model_name)
1202 if not model_info:
1203 raise RestInvalidDataTypeException(model_name)
1204
1205 # Set up the keyword argument dictionary we use to filter the QuerySet.
1206 filter_keyword_args = {}
1207
1208 jsonp_prefix = None
1209 nolist = False
1210 order_by = None
1211
1212 # Now iterate over the query params and add further filter keyword arguments
1213 query_param_dict = request.GET
1214 for query_param_name, query_param_value in query_param_dict.items():
1215 add_query_param = False
1216 query_param_name = str(query_param_name)
1217 query_param_value = str(query_param_value)
1218 if query_param_name == 'callback': #switching to match up with jquery getJSON call naming convention.
1219 jsonp_prefix = query_param_value
1220 elif query_param_name == 'nolist':
1221 if query_param_value not in ('False', 'false', '0', ''):
1222 nolist = True
1223 elif query_param_name == 'orderby':
1224 order_by = query_param_value.split(',')
1225 for i in range(len(order_by)):
1226 name = order_by[i]
1227 if name.startswith('-'):
1228 descending = True
1229 name = name[1:]
1230 else:
1231 descending = False
1232 field_info = model_info.rest_name_dict.get(name)
1233 if not field_info:
1234 raise RestInvalidOrderByException(name)
1235 name = field_info.name
1236 if descending:
1237 name = '-' + name
1238 order_by[i] = name
1239 elif query_param_name in model_info.rest_name_dict:
1240 field_info = model_info.rest_name_dict.get(query_param_name)
1241 # For booleans, translate True/False strings into 0/1.
1242 if field_info and type(field_info.django_field_info) == BooleanField:
1243 if query_param_value.lower() == 'false':
1244 query_param_value = 0
1245 elif query_param_value.lower() == 'true':
1246 query_param_value = 1
1247 query_param_name = field_info.name
1248 if model_name == 'controller-node' and \
1249 query_param_name == 'id' and query_param_value == 'localhost':
1250 query_param_value = get_local_controller_id()
1251 if model_name in 'controller-interface' and \
1252 query_param_name == 'controller' and \
1253 query_param_value == 'localhost':
1254 query_param_value = get_local_controller_id()
1255 add_query_param = True
1256 else:
1257 double_underscore_start = query_param_name.find("__")
1258 if double_underscore_start >= 0:
1259 rest_name = query_param_name[:double_underscore_start]
1260 field_info = model_info.rest_name_dict.get(rest_name)
1261 if field_info:
1262 operation = query_param_name[double_underscore_start:]
1263 query_param_name = field_info.name
1264 if type(field_info.django_field_info) == ForeignKey:
1265 query_param_name = query_param_name + '__' + field_info.django_field_info.rel.field_name
1266 # Substitute in the model field name for the (possible renamed) rest name
1267 query_param_name += operation
1268 add_query_param = True
1269 if add_query_param:
1270 filter_keyword_args[query_param_name] = query_param_value
1271
1272 if id != None:
1273 if len(filter_keyword_args) > 0:
1274 raise RestInvalidFilterParameterException(filter_keyword_args.keys()[0])
1275 try:
1276 get_args = {model_info.primary_key:id}
1277 instance = model_info.model_class.objects.get(**get_args)
1278 instance_list = (instance,)
1279 nolist = True
1280 except model_info.model_class.DoesNotExist,e:
1281 raise RestResourceNotFoundException(request.path)
1282 except model_info.model_class.MultipleObjectsReturned, exc:
1283 # traceback.print_exc()
1284 raise RestInternalException(exc)
1285 elif (request.method != 'PUT') or (len(filter_keyword_args) > 0):
1286 # Get the QuerySet based on the keyword arguments we constructed
1287 instance_list = model_info.model_class.objects.filter(**filter_keyword_args)
1288 if order_by:
1289 instance_list = instance_list.order_by(*order_by)
1290 else:
1291 # We're inserting new objects, so there's no need to do a query
1292 instance_list = None
1293
1294 response_content_type = JSON_CONTENT_TYPE
1295
1296 if request.method == 'GET':
1297 json_response_data = []
1298 for instance in instance_list:
1299 json_instance = {}
1300 for field_info in model_info.field_name_dict.values():
1301 # Made some minor edits to deal with a) fields that are empty and b) fields that are not strings -Kyle
1302 # Changed this to only do an explicit string conversion if it's a unicode string.
1303 # The controller is expecting to get the unstringified value (e.g. for boolean values)
1304 # Not sure if this will break things in the UI, but we'll need to resolve how
1305 # we want to handle this. Also, how do we want to handle unicode strings? -- robv
1306 field_name = field_info.name
1307 if type(field_info.django_field_info) == ForeignKey:
1308 field_name += '_id'
1309 value = instance.__dict__.get(field_name)
1310 if value != None:
1311 if field_info.json_serialize:
1312 value = str(value)
1313 json_instance[field_info.rest_name] = value
1314 json_response_data.append(json_instance)
1315
1316 # If the nolist query param was enabled then check to make sure
1317 # that there was only a single instance in the response list and,
1318 # if so, unpack it from the list
1319 if nolist:
1320 if len(json_response_data) != 1:
1321 raise RestNoListResultException()
1322 json_response_data = json_response_data[0]
1323
1324 # Convert to json
1325 response_data = simplejson.dumps(json_response_data)
1326
1327 # If the jsonp query parameter was specified, wrap the data with
1328 # the jsonp prefix
1329 if jsonp_prefix:
1330 response_data = jsonp_prefix + '(' + response_data + ')'
1331 # We don't really know what the content type is here, but it's typically javascript
1332 response_content_type = TEXT_JAVASCRIPT_CONTENT_TYPE
1333 elif request.method == 'PUT':
1334 response_data = get_successful_response_data('saved')
1335 response_content_type = JSON_CONTENT_TYPE
1336
1337 begin_batch_notification()
1338 json_object = simplejson.loads(request.raw_post_data)
1339 if instance_list is not None:
1340
1341 # don't allow the ip address of the first interface to
1342 # be updated once it is set. This really applies to
1343 # the interface cassandra uses to sync the db.
1344 if model_name == 'controller-interface':
1345 for instance in instance_list:
1346 if instance.number == 0 and instance.ip != '':
1347 if 'ip' in json_object and json_object['ip'] != instance.ip:
1348 raise RestModelException("In this version, ip-address of primary interface can't be updated after initial configuration")
1349
1350 # In this case the URL includes query parameter(s) which we assume
1351 # narrow the request to the instances of the model to be updated
1352 # updated with the PUT data. So we're updating existing instances
1353
1354 # If it's a list with one element then extract the single element
1355 if (type(json_object) == list) and (len(json_object) == 1):
1356 json_object = json_object[0]
1357
1358 # We're expecting a dictionary where the keys match the model field names
1359 # If the data isn't a dictionary then return an error
1360 if type(json_object) != dict:
1361 raise RestInvalidPutDataException() # TODO: Should return something different here
1362
1363 # Set the fields in the model instance with the data from the dictionary
1364 for instance in instance_list:
1365 for rest_name, value in json_object.items():
1366 if not rest_name in model_info.rest_name_dict:
1367 raise RestModelException("Model '%s' has no field '%s'" %
1368 (model_name, rest_name))
1369 field_info = model_info.rest_name_dict[rest_name]
1370 field_name = str(field_info.name) # FIXME: Do we need the str cast?
1371 if type(field_info.django_field_info) == ForeignKey:
1372 field_name += '_id'
1373 # TODO: Does Django still not like unicode strings here?
1374 if type(value) == unicode:
1375 value = str(value)
1376 instance.__dict__[field_name] = value
1377 # Save the updated model instance
1378 try:
1379 instance.full_clean()
1380 instance.save()
1381 except ValidationError, err:
1382 handle_validation_error(model_info, err)
1383 #raise RestValidationException(err)
1384 except Exception, exc:
1385 raise RestSaveException(exc)
1386 else:
1387 # In this case no query parameters or id were specified so we're inserting new
1388 # instances into the database. The PUT data can be either a list of new
1389 # items to add (i.e. top level json object is a list) or else a single
1390 # new element (i.e. top-level json object is a dict).
1391 #print "creating object(s)"
1392
1393 # To simplify the logic below we turn the single object case into a list
1394 if type(json_object) != list:
1395 json_object = [json_object]
1396
1397 # Create new model instances for all of the items in the list
1398 for instance_data_dict in json_object:
1399 # We expect the data to be a dictionary keyed by the field names
1400 # in the model. If it's not a dict return an error
1401 if type(instance_data_dict) != dict:
1402 raise RestInvalidPutDataException()
1403
1404 converted_dict = {}
1405
1406 # Now add the fields specified in the PUT data
1407 for rest_name, value in instance_data_dict.items():
1408
1409 #print " processing " + str(name) + " " + str(value)
1410
1411 if not rest_name in model_info.rest_name_dict:
1412 raise RestModelException("Model '%s' has no field '%s'" %
1413 (model_name, rest_name))
1414 field_info = model_info.rest_name_dict[rest_name]
1415 # simplejson uses unicode strings when it loads the objects which
1416 # Django doesn't like that, so we convert these to ASCII strings
1417 if type(rest_name) == unicode:
1418 rest_name = str(rest_name)
1419 if type(value) == unicode:
1420 value = str(value)
1421 field_name = field_info.name
1422 # FIXME: Hack to remap localhost controller node id alias to the actual
1423 # ID for the controller node. We shouldn't be doing this here (this code
1424 # shouldn't have anything about specific models), but it's the easiest
1425 # way to handle it for now and this code is likely going away sometime
1426 # pretty soon (written in May, 2012, let's see how long "pretty soon"
1427 # is :-) )
1428 if model_name == 'controller-node' and field_name == 'id' and value == 'localhost':
1429 value = get_local_controller_id()
1430 if type(field_info.django_field_info) == ForeignKey:
1431 field_name += '_id'
1432 converted_dict[field_name] = value
1433
1434 try:
1435 instance = model_info.model_class(**converted_dict)
1436 instance.full_clean()
1437 instance.save()
1438 except ValidationError, err:
1439 handle_validation_error(model_info, err)
1440 #raise RestValidationException(err)
1441 except Exception, e:
1442 # traceback.print_exc()
1443 raise RestSaveException(e)
1444
1445 end_batch_notification()
1446 elif request.method == 'DELETE':
1447 begin_batch_notification()
1448 for instance in instance_list:
1449 try:
1450 instance.delete()
1451 except ValidationError, err:
1452 handle_validation_error(model_info, err)
1453 except Exception, e:
1454 raise RestException(e)
1455 end_batch_notification()
1456 response_data = "deleted"
1457 response_content_type = 'text/plain'
1458 else:
1459 raise RestInvalidMethodException()
1460
1461 return HttpResponse(response_data, response_content_type)
1462
1463def synthetic_controller_interface(model_name, query_param_dict, json_response_data):
1464 # ---
1465 if model_name == 'controller-interface':
1466 # For controller-interfaces, when an ip address (netmask too)
1467 # is left unconfigured, then it may be possible to associate
1468 # ifconfig details with the interface.
1469 #
1470 # Since controller-interfaces has no mechanism to associate
1471 # specific ifconfig interfaces with rows, it's only possible to
1472 # associate ip's when a single unconfigured ip address exists,
1473 # using a process of elimination. For all ip address in the
1474 # ifconfig output, all statically configured controller-interface
1475 # items are removed. If only one result is left, and only
1476 # one controller-interface has an unconfigured ip address
1477 # (either a dhcp acquired address, or a static address where
1478 # the ip address is uncofigured), the ifconfig ip address
1479 # is very-likely to be the one associated with the
1480 # controller-interface row.
1481
1482 # Check the list of values to see if any are configured as dhcp
1483 dhcp_count = 0
1484 unconfigured_static_ip = 0
1485 this_host = get_local_controller_id()
1486
1487 for entry in json_response_data:
1488 if 'mode' in entry and entry['mode'] == 'dhcp' and \
1489 'controller' in entry and entry['controller'] == this_host:
1490 dhcp_count += 1
1491 if 'mode' in entry and entry['mode'] == 'static' and \
1492 'ip' in entry and entry['ip'] == '':
1493 unconfigured_static_ip += 1
1494 if dhcp_count + unconfigured_static_ip != 1:
1495 for entry in json_response_data:
1496 entry['found-ip'] = entry['ip']
1497 return
1498
1499 need_controller_query = False
1500 # determine whether the complete list of interfaces needs
1501 # to be collected to associate the dhcp address.
1502 for query_param_name, query_param_value in query_param_dict.items():
1503 if query_param_name != 'controller':
1504 need_controller_query = True
1505 if query_param_name == 'controller' and \
1506 query_param_value != this_host:
1507 need_controller_query = True
1508
1509 if need_controller_query == False:
1510 model_interfaces = [x for x in json_response_data
1511 if 'controller' in x and x['controller'] == this_host]
1512 else:
1513 # print 'need to collect all interfaces'
1514 filter_keyword_args = {'controller' : this_host}
1515 model_info = rest_model_info_dict.get(model_name)
1516 instance_list = model_info.model_class.objects.filter(**filter_keyword_args)
1517 response_data = []
1518 for instance in instance_list:
1519 data = {}
1520 for field_info in model_info.field_name_dict.values():
1521 field_name = field_info.name
1522 if type(field_info.django_field_info) == ForeignKey:
1523 field_name += '_id'
1524 value = instance.__dict__.get(field_name)
1525 if value != None:
1526 if field_info.json_serialize:
1527 value = str(value)
1528 data[field_info.rest_name] = value
1529 response_data.append(data)
1530 model_interfaces = response_data
1531
1532 # Recompute the number of dhcp configured interfaces,
1533 # model_interfaces is the collection of interface for 'this_host'
1534 dhcp_count = 0
1535 unconfigured_static_ip = 0
1536 for ifs in model_interfaces:
1537 if 'mode' in ifs and ifs['mode'] == 'dhcp':
1538 dhcp_count += 1
1539 if 'mode' in ifs and ifs['mode'] == 'static' and \
1540 'ip' in ifs and ifs['ip'] == '':
1541 unconfigured_static_ip += 1
1542
1543 if dhcp_count + unconfigured_static_ip != 1:
1544 # print "Sorry, %s dhcp + %s unconfigured static interfaces on %s" % \
1545 # (dhcp_count, unconfigured_static_ip, this_host)
1546 # copy over static ip's
1547 for entry in json_response_data:
1548 entry['found-ip'] = entry['ip']
1549 return
1550
1551 # collect current details for all the network interfaces
1552 inet4_ifs = _collect_system_interfaces()
1553
1554 # iterate over the model_interfaces's interfaces, and
1555 # remove ip addresses from inet4_ifs which are static, and
1556 # have the correct static value.
1557
1558 report_static = False
1559 match_id = ''
1560
1561 for ifs in model_interfaces:
1562 if 'mode' in ifs and ifs['mode'] == 'static':
1563 if 'ip' in ifs and ifs['ip'] == '':
1564 # print "Unconfigured static ip for %s", ifs['id']
1565 match_id = ifs['id']
1566 if 'ip' in ifs and ifs['ip'] != '':
1567 # find this address in the known addresses
1568 remove_entry = -1
1569 for index, inet4_if in enumerate(inet4_ifs):
1570 if inet4_if['addr'] == ifs['ip']:
1571 remove_entry = index
1572 break
1573 if remove_entry == -1:
1574 # print "Static ip %s not found" % ifs['ip']
1575 pass
1576 else:
1577 del inet4_ifs[remove_entry]
1578 elif 'mode' in ifs and ifs['mode'] == 'dhcp':
1579 match_id = ifs['id']
1580 else:
1581 # ought to assert here, not_reached()
1582 pass
1583
1584 # When only one entry is left in inet, its possible to do the assocation
1585 if len(inet4_ifs) != 1:
1586 # print "Incorrect number %s of inet4 interfaces left" % len(inet4_ifs)
1587 pass
1588
1589 for entry in json_response_data:
1590 entry['found-ip'] = entry['ip']
1591 entry['found-netmask'] = entry['netmask']
1592
1593 if entry['id'] == match_id:
1594 # make sure the address isn't set
1595 if entry['ip'] == '':
1596 entry['found-ip'] = inet4_ifs[0]['addr']
1597 entry['found-netmask'] = inet4_ifs[0]['netmask']
1598 entry['found-broadcast'] = inet4_ifs[0]['broadcast']
1599 else:
1600 # ought to assert here, not_reached()
1601 pass
1602
1603@safe_rest_view
1604def do_synthetic_instance(request, model_name, id=None):
1605
1606 if request.method != 'GET':
1607 raise RestInvalidMethodException()
1608
1609 # Lookup the model class associated with the specified name
1610 model_info = rest_model_info_dict.get(model_name)
1611 if not model_info:
1612 raise RestInvalidDataTypeException(model_name)
1613
1614 # Set up the keyword argument dictionary we use to filter the QuerySet.
1615 filter_keyword_args = {}
1616
1617 jsonp_prefix = None
1618 nolist = False
1619 order_by = None
1620
1621 # Now iterate over the query params and add further filter keyword arguments
1622 query_param_dict = request.GET
1623 for query_param_name, query_param_value in query_param_dict.items():
1624 add_query_param = False
1625 query_param_name = str(query_param_name)
1626 query_param_value = str(query_param_value)
1627 if query_param_name == 'callback': #switching to match up with jquery getJSON call naming convention.
1628 jsonp_prefix = query_param_value
1629 elif query_param_name == 'nolist':
1630 if query_param_value not in ('False', 'false', '0', ''):
1631 nolist = True
1632 elif query_param_name == 'orderby':
1633 order_by = query_param_value.split(',')
1634 for i in range(len(order_by)):
1635 name = order_by[i]
1636 if name.startswith('-'):
1637 descending = True
1638 name = name[1:]
1639 else:
1640 descending = False
1641 field_info = model_info.rest_name_dict.get(name)
1642 if not field_info:
1643 raise RestInvalidOrderByException(name)
1644 name = field_info.name
1645 if descending:
1646 name = '-' + name
1647 order_by[i] = name
1648 elif query_param_name in model_info.rest_name_dict:
1649 field_info = model_info.rest_name_dict.get(query_param_name)
1650 query_param_name = field_info.name
1651 add_query_param = True
1652 else:
1653 double_underscore_start = query_param_name.find("__")
1654 if double_underscore_start >= 0:
1655 rest_name = query_param_name[:double_underscore_start]
1656 field_info = model_info.rest_name_dict.get(rest_name)
1657 if field_info:
1658 operation = query_param_name[double_underscore_start:]
1659 query_param_name = field_info.name
1660 if type(field_info.django_field_info) == ForeignKey:
1661 query_param_name = query_param_name + '__' + field_info.django_field_info.rel.field_name
1662 # Substitute in the model field name for the (possible renamed) rest name
1663 query_param_name += operation
1664 add_query_param = True
1665 if add_query_param:
1666 filter_keyword_args[query_param_name] = query_param_value
1667
1668 if id != None:
1669 if len(filter_keyword_args) > 0:
1670 raise RestInvalidFilterParameterException(filter_keyword_args.keys()[0])
1671 try:
1672 get_args = {model_info.primary_key:id}
1673 instance = model_info.model_class.objects.get(**get_args)
1674 instance_list = (instance,)
1675 nolist = True
1676 except model_info.model_class.DoesNotExist,e:
1677 raise RestResourceNotFoundException(request.path)
1678 except model_info.model_class.MultipleObjectsReturned, exc:
1679 # traceback.print_exc()
1680 raise RestInternalException(exc)
1681 elif (request.method != 'PUT') or (len(filter_keyword_args) > 0):
1682 # Get the QuerySet based on the keyword arguments we constructed
1683 instance_list = model_info.model_class.objects.filter(**filter_keyword_args)
1684 if order_by:
1685 instance_list = instance_list.order_by(*order_by)
1686 else:
1687 # We're inserting new objects, so there's no need to do a query
1688 instance_list = None
1689
1690 response_content_type = JSON_CONTENT_TYPE
1691
1692 # Syntheric types only do requests --
1693 json_response_data = []
1694 for instance in instance_list:
1695 json_instance = {}
1696 for field_info in model_info.field_name_dict.values():
1697 # Made some minor edits to deal with a) fields that are empty and b) fields that are not strings -Kyle
1698 # Changed this to only do an explicit string conversion if it's a unicode string.
1699 # The controller is expecting to get the unstringified value (e.g. for boolean values)
1700 # Not sure if this will break things in the UI, but we'll need to resolve how
1701 # we want to handle this. Also, how do we want to handle unicode strings? -- robv
1702 field_name = field_info.name
1703 if type(field_info.django_field_info) == ForeignKey:
1704 field_name += '_id'
1705 value = instance.__dict__.get(field_name)
1706 if value != None:
1707 if field_info.json_serialize:
1708 value = str(value)
1709 json_instance[field_info.rest_name] = value
1710 json_response_data.append(json_instance)
1711
1712 # ---
1713 if model_name == 'controller-interface':
1714 synthetic_controller_interface(model_name, query_param_dict, json_response_data)
1715
1716 # Convert to json
1717 response_data = simplejson.dumps(json_response_data)
1718
1719 # If the nolist query param was enabled then check to make sure
1720 # that there was only a single instance in the response list and,
1721 # if so, unpack it from the list
1722 if nolist:
1723 if len(json_response_data) != 1:
1724 raise RestNoListResultException()
1725 json_response_data = json_response_data[0]
1726
1727 # If the jsonp query parameter was specified, wrap the data with
1728 # the jsonp prefix
1729 if jsonp_prefix:
1730 response_data = jsonp_prefix + '(' + response_data + ')'
1731 # We don't really know what the content type is here, but it's typically javascript
1732 response_content_type = TEXT_JAVASCRIPT_CONTENT_TYPE
1733
1734 return HttpResponse(response_data, response_content_type)
1735
1736@safe_rest_view
1737def do_user_data_list(request):
1738 # Now iterate over the query params and add any valid filter keyword arguments
1739 filter_keyword_args = {}
1740 for query_param_name, query_param_value in request.GET.items():
1741 query_param_name = str(query_param_name)
1742 double_underscore_start = query_param_name.find("__")
1743 if double_underscore_start >= 0:
1744 attribute_name = query_param_name[:double_underscore_start]
1745 else:
1746 attribute_name = query_param_name
1747
1748 # In the future, if we add support for things like mod_date, creation_date, etc.
1749 # which would be supported in query params, then they'd be added to this list/tuple.
1750 if attribute_name not in ('name',):
1751 raise RestInvalidFilterParameterException(query_param_name)
1752 filter_keyword_args[query_param_name] = query_param_value
1753
1754 instance_list = UserData.objects.filter(**filter_keyword_args)
1755
1756 if request.method == 'GET':
1757 user_data_info_list = []
1758
1759 # FIXME: robv: It's incorrect to *always* add this to the user data,
1760 # because it means we're not respecting the filter query parameters.
1761 # To work completely correctly we'd need to duplicate a lot of logic
1762 # for processing the query parameters, which would be tedious.
1763 # Should talk to Mandeep about why it was done this way. Maybe we
1764 # should expose these special cases in a different URL/view.
1765 for fn in ['startup-config', 'upgrade-config']:
1766 try:
1767 sc = "%s/run/%s" % (sdncon.SDN_ROOT, fn)
1768 f = open(sc, 'r')
1769 f.close()
1770 t = time.strftime("%Y-%m-%d.%H:%M:%S",
1771 time.localtime(os.path.getmtime(sc)))
1772 instance_name = fn + '/timestamp=' + t + \
1773 '/version=1/length=' + \
1774 str(os.path.getsize(sc))
1775 url_path = 'rest/v1/data/' + instance_name + '/'
1776
1777 user_data_info = { 'name' : instance_name,
1778 'url_path' : url_path, }
1779 user_data_info_list.append(user_data_info)
1780 except:
1781 pass
1782
1783 for instance in instance_list:
1784 user_data_info = {'name': instance.name,
1785 'url_path': 'rest/v1/data/' + instance.name + '/'}
1786 user_data_info_list.append(user_data_info)
1787
1788 response_data = simplejson.dumps(user_data_info_list)
1789 elif request.method == 'DELETE':
1790 instance_list.delete()
1791 response_data = {}
1792 response_data['status'] = 'success'
1793 response_data['message'] = 'user data deleted'
1794 response_data = simplejson.dumps(response_data)
1795 response_content_type = JSON_CONTENT_TYPE
1796 else:
1797 raise RestInvalidMethodException()
1798
1799 return HttpResponse(response_data, JSON_CONTENT_TYPE)
1800
1801@safe_rest_view
1802def do_user_data(request, name):
1803 query_param_dict = request.GET
1804 #
1805 # Manage startup-config/update-config differently
1806 if name.find('/') >= 0 and \
1807 name.split('/')[0] in ['startup-config', 'upgrade-config']:
1808 path = "%s/run/%s" % (sdncon.SDN_ROOT, name.split('/')[0])
1809 response_data = {}
1810
1811 if request.method == 'GET':
1812 with open(path, 'r') as f:
1813 response_data = f.read()
1814 response_content_type = "text/plain"
1815 elif request.method == 'PUT':
1816 try:
1817 with open(path, 'w') as f:
1818 f.write(request.raw_post_data)
1819 response_data['status'] = 'success'
1820 response_data['message'] = 'user data updated'
1821 except:
1822 response_data['status'] = 'failure'
1823 response_data['message'] = "can't write file"
1824 response_content_type = JSON_CONTENT_TYPE
1825 response_data = simplejson.dumps(response_data)
1826 elif request.method == 'DELETE':
1827 try:
1828 f = open(path, "r")
1829 f.close()
1830 except:
1831 raise RestResourceNotFoundException(request.path)
1832
1833 try:
1834 os.remove(path)
1835 response_data['status'] = 'success'
1836 response_data['message'] = 'user data deleted'
1837 except:
1838 response_data['status'] = 'failure'
1839 response_data['message'] = "can't delete file"
1840 response_data = simplejson.dumps(response_data)
1841 response_content_type = JSON_CONTENT_TYPE
1842 else:
1843 raise RestInvalidMethodException()
1844
1845 return HttpResponse(response_data, response_content_type)
1846
1847
1848 # Default values for optional query parameters
1849 #private = False
1850 binary = False
1851
1852 for param_name, param_value in query_param_dict.items():
1853 if param_name == 'binary':
1854 if request.method != 'PUT':
1855 raise RestInvalidQueryParameterException(name)
1856 binary = param_value.lower() == 'true' or param_value == '1'
1857 #elif param_name == 'private':
1858 # private = param_value
1859 else:
1860 raise RestInvalidQueryParameterException(param_name)
1861
1862 # FIXME: Need HTTP basic/digest auth support for the following
1863 # code to work.
1864 #if private:
1865 # user = request.user
1866 #else:
1867 # user = None
1868 #if user != None and not user.is_authenticated():
1869 # raise RestAuthenticationRequiredException()
1870 user = None
1871
1872 # There's currently an issue with filtering on the user when using the
1873 # Cassandra database backend. Since we don't support private per-user
1874 # data right now, I'm just disabling filtering on the user and only
1875 # filter on the name
1876 #user_data_query_set = UserData.objects.filter(user=user, name=name)
1877 user_data_query_set = UserData.objects.filter(name=name)
1878
1879 count = user_data_query_set.count()
1880 if count > 1:
1881 raise RestInternalException('Duplicate user data values for the same name')
1882
1883 if request.method == 'GET':
1884 if count == 0:
1885 raise RestResourceNotFoundException(request.path)
1886 user_data = user_data_query_set[0]
1887 response_data = user_data.data
1888 if user_data.binary:
1889 response_data = base64.b64decode(response_data)
1890 response_content_type = user_data.content_type
1891 elif request.method == 'PUT':
1892 content_type = request.META['CONTENT_TYPE']
1893 if content_type == None:
1894 if binary:
1895 content_type = BINARY_DATA_CONTENT_TYPE
1896 else:
1897 content_type = JSON_CONTENT_TYPE
1898 response_data = {}
1899 if count == 1:
1900 response_data['status'] = 'success'
1901 response_data['message'] = 'user data updated'
1902 user_data = user_data_query_set[0]
1903 else:
1904 response_data['status'] = 'success'
1905 response_data['message'] = 'user data created'
1906 user_data = UserData(user=user,name=name)
1907 user_data.binary = binary
1908 user_data.content_type = content_type
1909 data = request.raw_post_data
1910 if binary:
1911 data = base64.b64encode(data)
1912 user_data.data = data
1913 user_data.save()
1914 response_data = simplejson.dumps(response_data)
1915 response_content_type = JSON_CONTENT_TYPE
1916 elif request.method == 'DELETE':
1917 if count == 0:
1918 raise RestResourceNotFoundException(request.path)
1919 user_data = user_data_query_set[0]
1920 user_data.delete()
1921 response_data = {}
1922 response_data['status'] = 'success'
1923 response_data['message'] = 'user data deleted'
1924 response_data = simplejson.dumps(response_data)
1925 response_content_type = JSON_CONTENT_TYPE
1926 else:
1927 raise RestInvalidMethodException()
1928
1929 return HttpResponse(response_data, response_content_type)
1930
1931@safe_rest_view
1932def do_sdnplatform_tunnel_manager(request, dpid=None):
1933 """
1934 This returns realtime statistics from sdnplatform
1935 """
1936
1937 if request.method != 'GET':
1938 raise RestInvalidMethodException()
1939 if dpid == None:
1940 raise RestInvalidMethodException()
1941
1942 print 'DPID', dpid
1943 if dpid == 'all':
1944 url = controller_url('vns', 'tunnel-manager', 'all', 'json')
1945 else:
1946 url = controller_url('vns', 'tunnel-manager', 'switch='+dpid, 'json')
1947
1948 response_text = urllib2.urlopen(url).read()
1949 entries = simplejson.loads(response_text)
1950
1951 if 'error' in entries and entries['error'] != None:
1952 RestInternalException(entries['error'])
1953
1954 return HttpResponse(json.dumps(entries['tunnMap']), JSON_CONTENT_TYPE)
1955
1956@safe_rest_view
1957def do_sdnplatform_controller_summary(request):
1958 """
1959 This returns summary statistics from sdnplatform modules
1960 """
1961 if request.method != 'GET':
1962 raise RestInvalidMethodException()
1963
1964 url = controller_url('core', 'controller', 'summary', 'json')
1965 return get_sdnplatform_response(url)
1966
1967def filter_queries(choice_list, param_dict):
1968 return dict([[x, param_dict[x]] for x in choice_list
1969 if x in param_dict and param_dict[x] != 'all'])
1970
1971@safe_rest_view
1972def do_reload(request):
1973 """
1974 This calls an oswrapper that reloads the box.
1975 """
1976 if request.method != 'GET':
1977 raise RestInvalidMethodException()
1978 exec_os_wrapper("ReloadController", 'set', [])
1979 response_text = '{"status":"reloading"}'
1980 return HttpResponse(response_text, JSON_CONTENT_TYPE)
1981
1982@safe_rest_view
1983def do_resetbsc(request):
1984 """
1985 This calls an oswrapper that resets the box.
1986 """
1987 if request.method != 'PUT':
1988 raise RestInvalidMethodException()
1989 exec_os_wrapper("ResetBsc", 'set', [])
1990 response_text = '{"status":"resetting"}'
1991 return HttpResponse(response_text, JSON_CONTENT_TYPE)
1992
1993@safe_rest_view
1994def do_abort_upgrade(request):
1995 """
1996 This calls an oswrapper that reloads the box.
1997 """
1998 if request.method != 'PUT':
1999 raise RestInvalidMethodException()
2000 controller_id = get_local_controller_id()
2001 controller = Controller.objects.get(id=controller_id)
2002 if controller.status != 'Upgrading':
2003 raise RestUpgradeException("No Upgrade pending")
2004 exec_os_wrapper("AbortUpgrade", 'set', [])
2005 controller.status = 'Ready'
2006 controller.save()
2007 response_text = '{"status":"Ready"}'
2008 return HttpResponse(response_text, JSON_CONTENT_TYPE)
2009
2010@safe_rest_view
2011def do_config_rollback(request):
2012 data = simplejson.loads(request.raw_post_data)
2013 path = data['path']
2014 print "Executing config rollback with config @", path
2015
2016 if request.method != 'PUT':
2017 raise RestInvalidMethodException()
2018 exec_os_wrapper("RollbackConfig", 'set', [path])
2019 response_text = get_successful_response_data('prepared config rollbacked')
2020 return HttpResponse(response_text, JSON_CONTENT_TYPE)
2021
2022@safe_rest_view
2023def do_upload_data(request):
2024 if request.method != 'PUT':
2025 raise RestInvalidMethodException()
2026 data = simplejson.loads(request.raw_post_data)
2027 content = data['data']
2028 path = data['dst']
2029 print "Executing config rollback with config @", path
2030
2031 exec_os_wrapper("WriteDataToFile", 'set', [content, path])
2032 response_text = get_successful_response_data('written data')
2033 return HttpResponse(response_text, JSON_CONTENT_TYPE)
2034
2035@safe_rest_view
2036def do_diff_config(request):
2037 if request.method != 'PUT':
2038 raise RestInvalidMethodException()
2039 data = simplejson.loads(request.raw_post_data)
2040 config1 = data['config-1']
2041 config2 = data['config-2']
2042 print "diffing '%s' with '%s'" %(config1, config2)
2043
2044 result = exec_os_wrapper("DiffConfig", 'set', [config1, config2])
2045 return HttpResponse(simplejson.dumps(result), JSON_CONTENT_TYPE)
2046
2047@safe_rest_view
2048def do_extract_upgrade_pkg_manifest(request):
2049 """
2050 This calls an oswrapper that extracts the upgrade package.
2051 This returns the install package 'manifest'.
2052 """
2053 if request.method != 'GET':
2054 raise RestInvalidMethodException()
2055 exec_os_wrapper("GetLatestUpgradePkg", 'get', [])
2056 output = exec_os_wrapper("CatUpgradeImagesFile", 'get')
2057 upgradePkg = output['out'].strip()
2058 exec_os_wrapper("ExtractUpgradePkgManifest", 'set', [upgradePkg])
2059 output = exec_os_wrapper("ExtractUpgradePkgManifest", 'get')
2060 manifest = output['out']
2061 return HttpResponse(manifest, JSON_CONTENT_TYPE)
2062
2063@safe_rest_view
2064def do_extract_upgrade_pkg(request):
2065 if request.method != 'GET':
2066 raise RestInvalidMethodException()
2067 exec_os_wrapper("GetLatestUpgradePkg", 'get', [])
2068 output = exec_os_wrapper("CatUpgradeImagesFile", 'get')
2069 upgradePkg = output['out'].strip()
2070 exec_os_wrapper("ExtractUpgradePkg", 'set', [upgradePkg])
2071 return HttpResponse('{"status": "OK"}', JSON_CONTENT_TYPE)
2072
2073@safe_rest_view
2074def do_get_upgrade_pkg(request):
2075 """
2076 This calls an oswrapper to get the latest upgrade
2077 package uploaded to the controller.
2078 """
2079 if request.method != 'GET':
2080 raise RestInvalidMethodException()
2081 exec_os_wrapper("GetLatestUpgradePkg", 'get')
2082 result = exec_os_wrapper("CatUpgradeImagesFile", 'get')
2083 jsondict = {'file': result['out'].strip()}
2084 return HttpResponse(simplejson.dumps(jsondict), JSON_CONTENT_TYPE)
2085
2086@safe_rest_view
2087def do_cleanup_old_pkgs(request):
2088 if request.method != 'GET':
2089 raise RestInvalidMethodException()
2090 exec_os_wrapper("CleanupOldUpgradeImages", 'get')
2091 return HttpResponse('{"status": "OK"}', JSON_CONTENT_TYPE)
2092
2093@safe_rest_view
2094def do_execute_upgrade_step(request):
2095 """
2096 Executes a particular upgrade step according to the
2097 upgrade package manifest.
2098 """
2099 if request.method != 'PUT':
2100 raise RestInvalidMethodException()
2101
2102 put_data = json.loads(request.raw_post_data)
2103 imageName = put_data.get('imageName')
2104 stepNum = put_data.get('step')
2105 force = put_data.get('force')
2106
2107 args = [stepNum, imageName]
2108 if force:
2109 args.append("--force")
2110 result = exec_os_wrapper("ExecuteUpgradeStep", 'get',
2111 args)
2112 jsondict = {}
2113 if len(str(result['err']).strip()) > 0:
2114 jsondict['status'] = "ERROR"
2115 jsondict['description'] = str(result['err']).strip()
2116 else:
2117 jsondict['status'] = "OK"
2118 jsondict['description'] = str(result['out']).strip()
2119
2120 return HttpResponse(simplejson.dumps(jsondict), JSON_CONTENT_TYPE)
2121