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