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