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