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