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