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