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