Srikanth Vavilapalli | 1725e49 | 2014-12-01 17:50:52 -0800 | [diff] [blame^] | 1 | #!/usr/bin/python |
| 2 | # |
| 3 | # Copyright (c) 2010,2013 Big Switch Networks, Inc. |
| 4 | # |
| 5 | # Licensed under the Eclipse Public License, Version 1.0 (the |
| 6 | # "License"); you may not use this file except in compliance with the |
| 7 | # License. You may obtain a copy of the License at |
| 8 | # |
| 9 | # http://www.eclipse.org/legal/epl-v10.html |
| 10 | # |
| 11 | # Unless required by applicable law or agreed to in writing, software |
| 12 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
| 14 | # implied. See the License for the specific language governing |
| 15 | # permissions and limitations under the License. |
| 16 | # |
| 17 | |
| 18 | import subprocess |
| 19 | import os, atexit |
| 20 | import sys, traceback # traceback.print_exc() |
| 21 | from optparse import OptionParser |
| 22 | from types import StringType |
| 23 | import datetime |
| 24 | import json |
| 25 | import traceback |
| 26 | import re |
| 27 | import time |
| 28 | import urllib2 |
| 29 | import httplib # provides error processing for isinstance |
| 30 | import socket |
| 31 | |
| 32 | from prettyprint import PrettyPrinter |
| 33 | from storeclient import StoreClient |
| 34 | from climodelinfo import CliModelInfo |
| 35 | from vendor import VendorDB |
| 36 | |
| 37 | class ParamException(Exception): |
| 38 | def __init__(self, value): |
| 39 | self.value = value |
| 40 | def __str__(self): |
| 41 | return repr(self.value) |
| 42 | |
| 43 | TIMESTAMP_HELP = \ |
| 44 | ('Enter an integer timestamp in milliseconds since the epoch or formatted\n' + |
| 45 | 'as one of the following options in your local timezone:\n' |
| 46 | '"YYYY-MM-DD HH:MM:SS" YYYY-MM-DDTHH:MM:SS\n' + |
| 47 | '"YYYY-MM-DD HH:MM:SS+TTT" YYYY-MM-DDTHH:MM:SS+TTT\n' + |
| 48 | 'YYYY-MM-DD MM-DD\n' + |
| 49 | 'HH:MM now') |
| 50 | |
| 51 | |
| 52 | class Validate(): |
| 53 | |
| 54 | # initialize in init_obj_type_info |
| 55 | obj_type_info_dict = {} # see init_obj_type_info_dict |
| 56 | obj_types = [] |
| 57 | obj_keys = {} |
| 58 | |
| 59 | known_controllers = ["127.0.0.1:8000"] |
| 60 | controller = None |
| 61 | # LOOK! manage this separately until we can for sure talk to eth0 addr instead of 127.0.0.1 |
| 62 | controller_for_prompt = "127.0.0.1:8000" |
| 63 | cluster = "default" |
| 64 | |
| 65 | run = True |
| 66 | |
| 67 | # Cached options for last completion |
| 68 | last_line = None |
| 69 | last_options = None |
| 70 | |
| 71 | # Are we running in batch mode, i.e. "cat commands | cli.py" |
| 72 | batch = False |
| 73 | |
| 74 | # used for parsing as an arg |
| 75 | local_name_pattern = "localhost://([A-Za-z0-9_:@\-\.\/]+$)" |
| 76 | |
| 77 | # contained objects |
| 78 | pp = None |
| 79 | store = None |
| 80 | model_handler = None |
| 81 | |
| 82 | # |
| 83 | # -------------------------------------------------------------------------------- |
| 84 | # init_obj_type_info_dict |
| 85 | # |
| 86 | # Builds the init_obj_type_info_dict, which is a dictionary indexed by |
| 87 | # the current mode, which also results in a dictionary, then indexed |
| 88 | # by a either a 'field' or 'field_ordering' key. |
| 89 | # |
| 90 | # When init_obj_type_info_dict is indexed by the table/model name, the |
| 91 | # value is a dictionary. That dictionary has several keys: |
| 92 | # - 'fields' |
| 93 | # - 'field_orderings' |
| 94 | # |
| 95 | # The 'fields' indexed result returns a dictionary, each of which |
| 96 | # is indexed by the names of the fields within the table/model. further |
| 97 | # indicies describe details about that field within the table: |
| 98 | # - 'formatter' desscribes the prettyPrinter function for that field |
| 99 | # - 'primary_key' identifes this field as the key for the table/model. |
| 100 | # - 'verbose_name' identfies an additonal name for the field, inteneded |
| 101 | # to be more descriptive |
| 102 | # - 'has_rest_model' True/False |
| 103 | # - 'json_serialize_string' True/False |
| 104 | # - 'help_text' String providing more clues about the intent of this field |
| 105 | # |
| 106 | # The 'field_ordering' dictionary associated with the name of the table/model |
| 107 | # is used to order the output. |
| 108 | # |
| 109 | # Also populates the self.obj_keys[], which is a dictionary mapping the table/model |
| 110 | # name to the name of the storage key (table/model's column) for that table. |
| 111 | # |
| 112 | def init_obj_type_info_dict(self): |
| 113 | self.cli_model_info = CliModelInfo() |
| 114 | self.obj_type_info_dict = self.cli_model_info.get_complete_obj_type_info_dict() |
| 115 | self.obj_types = [k for (k,v) in self.obj_type_info_dict.items() |
| 116 | if 'has_rest_model' in v] |
| 117 | for (k,d) in self.obj_type_info_dict.items(): |
| 118 | candidate_keys = [f for f in d['fields'].keys() |
| 119 | if d['fields'][f].get('primary_key', False)] |
| 120 | if len(candidate_keys) > 0: |
| 121 | self.obj_keys[k] = candidate_keys[0] |
| 122 | |
| 123 | |
| 124 | # |
| 125 | # -------------------------------------------------------------------------------- |
| 126 | # make_unit_formatter |
| 127 | # |
| 128 | def make_unit_formatter(self, units): |
| 129 | return lambda x,y: '%s %s' % (x, units) |
| 130 | |
| 131 | # |
| 132 | # -------------------------------------------------------------------------------- |
| 133 | # init_obj_type_info_dict |
| 134 | # |
| 135 | # Builds the init_obj_type_info_dict, which is a dictionary indexed by |
| 136 | # the current mode, which also results in a dictionary, then indexed |
| 137 | # by a either a 'field' or 'field_ordering' key. |
| 138 | # |
| 139 | # When init_obj_type_info_dict is indexed by the table/model name, the |
| 140 | # value is a dictionary. That dictionary has several keys: |
| 141 | # - 'fields' |
| 142 | # - 'field_orderings' |
| 143 | # |
| 144 | # The 'fields' indexed result returns a dictionary, each of which |
| 145 | # is indexed by the names of the fields within the table/model. further |
| 146 | # indicies describe details about that field within the table: |
| 147 | # - 'formatter' desscribes the prettyPrinter function for that field |
| 148 | # - 'primary_key' identifes this field as the key for the table/model. |
| 149 | # - 'verbose_name' identfies an additonal name for the field, inteneded |
| 150 | # to be more descriptive |
| 151 | # - 'has_rest_model' True/False |
| 152 | # - 'json_serialize_string' True/False |
| 153 | # - 'help_text' String providing more clues about the intent of this field |
| 154 | # |
| 155 | # The 'field_ordering' dictionary associated with the name of the table/model |
| 156 | # is used to order the output. |
| 157 | # |
| 158 | # Also populates the self.obj_keys[], which is a dictionary mapping the table/model |
| 159 | # name to the name of the storage key (table/model's column) for that table. |
| 160 | # |
| 161 | def init_obj_type_info_dict(self): |
| 162 | self.cli_model_info = CliModelInfo() |
| 163 | self.obj_type_info_dict = self.cli_model_info.get_complete_obj_type_info_dict() |
| 164 | self.obj_types = [k for (k,v) in self.obj_type_info_dict.items() |
| 165 | if 'has_rest_model' in v] |
| 166 | for (k,d) in self.obj_type_info_dict.items(): |
| 167 | candidate_keys = [f for f in d['fields'].keys() |
| 168 | if d['fields'][f].get('primary_key', False)] |
| 169 | if len(candidate_keys) > 0: |
| 170 | self.obj_keys[k] = candidate_keys[0] |
| 171 | |
| 172 | # |
| 173 | # -------------------------------------------------------------------------------- |
| 174 | # init |
| 175 | # |
| 176 | def init(self): |
| 177 | |
| 178 | self.vendordb = VendorDB() |
| 179 | self.vendordb.init() |
| 180 | |
| 181 | parser = OptionParser() |
| 182 | parser.add_option("-c", "--controller", dest="controller", |
| 183 | help="set default controller to CONTROLLER", |
| 184 | metavar="CONTROLLER", default=self.controller) |
| 185 | (options, args) = parser.parse_args() |
| 186 | self.controller = options.controller |
| 187 | if not self.controller: |
| 188 | self.controller = "127.0.0.1:8000" |
| 189 | |
| 190 | if not sys.stdin.isatty(): |
| 191 | self.batch = True |
| 192 | |
| 193 | self.init_obj_type_info_dict() |
| 194 | |
| 195 | self.pp = PrettyPrinter(self.obj_type_info_dict) |
| 196 | self.store = StoreClient() |
| 197 | self.store.set_controller(self.controller) |
| 198 | |
| 199 | |
| 200 | # |
| 201 | # -------------------------------------------------------------------------------- |
| 202 | # parse_optional_parameters |
| 203 | # |
| 204 | def parse_optional_parameters(self, params, words): |
| 205 | parsed = {} |
| 206 | i = 0 |
| 207 | while i < len(words): |
| 208 | word = words[i] |
| 209 | possible = [x for x in params if x.startswith(word)] |
| 210 | if len(possible) == 0: |
| 211 | raise ParamException('unknown option: %s' % word) |
| 212 | elif len(possible) > 1: |
| 213 | raise ParamException('ambiguous option: %s\n%s' % |
| 214 | (word, "\n".join(possible))) |
| 215 | else: |
| 216 | param_name = possible[0] |
| 217 | param = params[param_name] |
| 218 | if (param['type'] == 'flag'): |
| 219 | parsed[param_name] = True |
| 220 | else: |
| 221 | if i+1 < len(words): |
| 222 | argument = words[i+1] |
| 223 | if (param['type'] == 'string'): |
| 224 | parsed[param_name] = argument |
| 225 | elif (param['type'] == 'int'): |
| 226 | try: |
| 227 | parsed[param_name] = int(argument) |
| 228 | except ValueError: |
| 229 | raise ParamException('option %s requires ' + |
| 230 | 'integer argument' |
| 231 | % word) |
| 232 | elif (param['type'] == 'enum'): |
| 233 | arg_possible = [x |
| 234 | for x in param['values'] |
| 235 | if x.startswith(argument)] |
| 236 | if (len(arg_possible) == 0): |
| 237 | raise ParamException('option %s value must be in (%s)' % |
| 238 | (word,", ".join(param['values']))) |
| 239 | elif (len(arg_possible) > 1): |
| 240 | raise ParamException('ambiguous option %s value:\n%s' % |
| 241 | (word, "\n".join(arg_possible))) |
| 242 | else: |
| 243 | parsed[param_name] = arg_possible[0] |
| 244 | i += 1 |
| 245 | else: |
| 246 | raise ParamException('option %s requires an argument' |
| 247 | % word) |
| 248 | i += 1 |
| 249 | return parsed |
| 250 | |
| 251 | # |
| 252 | # -------------------------------------------------------------------------------- |
| 253 | # rest_error_to_dict |
| 254 | # Turn an exception into an error dictionary, which can later be printed. |
| 255 | # using rest_error_dict_to_message(). |
| 256 | # |
| 257 | def rest_error_to_dict(self, e, detail=None): |
| 258 | errors = None |
| 259 | # try to identifify validation requests |
| 260 | if isinstance(e, httplib.BadStatusLine): |
| 261 | errors = {'connection_error' : 'REST API server %s: ' |
| 262 | 'server not running' % |
| 263 | self.controller} |
| 264 | return errors |
| 265 | |
| 266 | elif isinstance(e, urllib2.HTTPError): |
| 267 | code = e.code |
| 268 | error_returned = e.readline() |
| 269 | |
| 270 | if code == 404: |
| 271 | if detail: |
| 272 | errors = {'not_found_error' : 'Not Found: %s' % detail} |
| 273 | else: |
| 274 | errors = {'not_found_error' : 'Not Found: %s' % error_returned} |
| 275 | elif code == 500 or code == 403: |
| 276 | errors = {'connection_error' : 'REST API server %s unable to connect: ' |
| 277 | 'Cassandra possibly not running' % |
| 278 | self.controller} |
| 279 | elif code == 400: |
| 280 | try: |
| 281 | errors = json.loads(error_returned) |
| 282 | except: |
| 283 | # if the error can't be converted into a dictionary, then imbed the complete |
| 284 | # errors as the value for a specific error key. |
| 285 | errors = {'error_result_error': "Can't convert returned error: %s" % error_returned} |
| 286 | pass |
| 287 | else: |
| 288 | errors = {'unknown_error': 'HttpError %s' % error_returned} |
| 289 | else: |
| 290 | errors = {'unknown_error': "Need error managmenet for error %s" % type(e)} |
| 291 | |
| 292 | return errors |
| 293 | |
| 294 | # |
| 295 | # -------------------------------------------------------------------------------- |
| 296 | # rest_error_dict_to_message |
| 297 | # Turn an rest_error_dict returned from rest_error_to_dict |
| 298 | # into an error message which can ge printed. Code assumes multiple errors |
| 299 | # won't occur; if a 'field_error' exists, for example, a 'model_error' won't |
| 300 | # also be posted in the error |
| 301 | # |
| 302 | def rest_error_dict_to_message(self, rest_error_dict): |
| 303 | error_msg = "" |
| 304 | if 'field_errors' in rest_error_dict: |
| 305 | for (k,v) in rest_error_dict['field_errors'].items(): |
| 306 | error_msg += "Syntax error: field %s: %s" % (k,v) |
| 307 | # traceback.print_stack(), to find out why the error occured |
| 308 | elif 'model_error' in rest_error_dict: |
| 309 | error_msg += "Error: %s" % rest_error_dict['model_error'] |
| 310 | elif 'not_found_error' in rest_error_dict: |
| 311 | error_msg += "Error: %s" % rest_error_dict['not_found_error'] |
| 312 | elif 'connection_error' in rest_error_dict: |
| 313 | error_msg += rest_error_dict['connection_error'] |
| 314 | elif 'error_result_error' in rest_error_dict: |
| 315 | error_msg += rest_error_dict['error_result_error'] |
| 316 | elif 'unknown_error' in rest_error_dict: |
| 317 | error_msg += rest_error_dict['unknown_error'] |
| 318 | else: |
| 319 | error_msg = "REST API server on controller-node %s " % self.controller |
| 320 | error_msg += "had %s error:\n" % rest_error_dict['error_type'] |
| 321 | error_msg += rest_error_dict['description'] |
| 322 | return error_msg |
| 323 | |
| 324 | # |
| 325 | # -------------------------------------------------------------------------------- |
| 326 | # method_from_name |
| 327 | # |
| 328 | def method_from_name(self, name): |
| 329 | return getattr(self, "validate_" + name.replace("-","_"), None) |
| 330 | |
| 331 | # |
| 332 | # -------------------------------------------------------------------------------- |
| 333 | # validate_port |
| 334 | # |
| 335 | # - validate port foreign key (to switch) |
| 336 | # |
| 337 | def validate_port(self): |
| 338 | error = None |
| 339 | print "validate_port" |
| 340 | |
| 341 | # collect known switches |
| 342 | switch_ids = None |
| 343 | switch_key = self.obj_keys["switch"] |
| 344 | try: |
| 345 | switch_table = self.store.get_table_from_store("switch") |
| 346 | switch_ids = [x[switch_key] for x in switch_table] |
| 347 | except Exception, e: |
| 348 | error = self.rest_error_to_dict(e) |
| 349 | print self.rest_error_dict_to_message(error) |
| 350 | pass |
| 351 | |
| 352 | if error: |
| 353 | print "Unable to collect switch, no switch dpid validation for port table" |
| 354 | error = None |
| 355 | |
| 356 | # collect known ports |
| 357 | port_table = None |
| 358 | port_key = self.obj_keys["port"] |
| 359 | try: |
| 360 | port_table = self.store.get_table_from_store("port") |
| 361 | except Exception, e: |
| 362 | error = self.rest_error_to_dict(e) |
| 363 | print self.rest_error_dict_to_message(error) |
| 364 | pass |
| 365 | |
| 366 | if error: |
| 367 | print "Unable to collect ports" |
| 368 | return |
| 369 | |
| 370 | for port in port_table: |
| 371 | if not port_key in port: |
| 372 | print "No port id in row" |
| 373 | else: |
| 374 | port_id = port[port_key] |
| 375 | if not 'switch' in port: |
| 376 | print 'port %s No switch in port (foreign key)' % port_id |
| 377 | |
| 378 | # |
| 379 | # -------------------------------------------------------------------------------- |
| 380 | # validate_switch |
| 381 | # |
| 382 | # - validate switch foreign key (to controller) |
| 383 | # |
| 384 | def validate_switch(self): |
| 385 | print "validate_switch" |
| 386 | error = None |
| 387 | |
| 388 | # collect known controllers |
| 389 | controller_ids = None |
| 390 | controller_key = self.obj_keys["controller-node"] |
| 391 | try: |
| 392 | controller_table = self.store.get_table_from_store("controller-node") |
| 393 | controller_ids = [x[contoller_key] for x in controller_table if controller_key in controller_table] |
| 394 | except Exception, e: |
| 395 | error = self.rest_error_to_dict(e) |
| 396 | print self.rest_error_dict_to_message(error) |
| 397 | pass |
| 398 | |
| 399 | if error: |
| 400 | print "Unable to collect controller, no controller validation for switches" |
| 401 | error = None |
| 402 | if len(controller_ids) == 0: |
| 403 | print "Unable to collect any controller ids" |
| 404 | |
| 405 | # collect known ports |
| 406 | switch_table = None |
| 407 | switch_key = self.obj_keys["switch"] |
| 408 | try: |
| 409 | switch_table = self.store.get_table_from_store("switch") |
| 410 | except Exception, e: |
| 411 | error = self.rest_error_to_dict(e) |
| 412 | print self.rest_error_dict_to_message(error) |
| 413 | pass |
| 414 | |
| 415 | if error: |
| 416 | print "Unable to collect switches" |
| 417 | return |
| 418 | |
| 419 | if len(switch_table) == 0: |
| 420 | print "switch table empty" |
| 421 | |
| 422 | for switch in switch_table: |
| 423 | if not switch_key in switch: |
| 424 | print "No switch id in row" |
| 425 | else: |
| 426 | switch_id = switch[switch_key] |
| 427 | if not 'switch' in switch: |
| 428 | print 'switch %s No controller foreign key' % switch_id |
| 429 | else: |
| 430 | controller = switch['controller'] |
| 431 | if not controller in controller_ids: |
| 432 | print "switch %s missing controller (foreign key) %s " % (switch_id, controller) |
| 433 | |
| 434 | # |
| 435 | # -------------------------------------------------------------------------------- |
| 436 | # validate_host_vns_interface |
| 437 | # |
| 438 | # - validate host-vns-interface foreigb key (to vns-interface) |
| 439 | # - validate host-vns-interface foreigb key (to host) |
| 440 | # - crack the id into three fragments, and validate each of the |
| 441 | # fragments references the expected componont (ie: matches the foreign key) |
| 442 | # |
| 443 | def validate_host_vns_interface(self): |
| 444 | print "host_vns_interface" |
| 445 | |
| 446 | error = None |
| 447 | # collect host's |
| 448 | host_ids = None |
| 449 | host_key = self.obj_keys["host"] |
| 450 | try: |
| 451 | host_table = self.store.get_table_from_store("host") |
| 452 | host_ids = [x[host_key] for x in host_table] |
| 453 | except Exception, e: |
| 454 | error = self.rest_error_to_dict(e) |
| 455 | print self.rest_error_dict_to_message(error) |
| 456 | pass |
| 457 | |
| 458 | if error: |
| 459 | print "Unable to collect hosts, no host-vns-interface host validation" |
| 460 | error = None |
| 461 | |
| 462 | # collect vns-interfaces |
| 463 | vns_interface_ids = None |
| 464 | vns_interface_key = self.obj_keys["vns-interface"] |
| 465 | try: |
| 466 | vns_interface_table = self.store.get_table_from_store("vns-interface") |
| 467 | vns_interface_ids = [x[vns_interface_key] for x in vns_interface_table] |
| 468 | except Exception, e: |
| 469 | error = self.rest_error_to_dict(e) |
| 470 | print self.rest_error_dict_to_message(error) |
| 471 | pass |
| 472 | |
| 473 | if error: |
| 474 | print "Unable to collect vns-interfaces, no host-vns-interface validation for vns-interfaces" |
| 475 | error = None |
| 476 | |
| 477 | # collect host-vns-interface |
| 478 | host_vns_interface_ids = None |
| 479 | host_vns_interface_key = self.obj_keys["host-vns-interface"] |
| 480 | try: |
| 481 | host_vns_interface_table = self.store.get_table_from_store("host-vns-interface") |
| 482 | host_vns_interface_ids = [x[host_vns_interface_key] for x in host_vns_interface_table] |
| 483 | except Exception, e: |
| 484 | error = self.rest_error_to_dict(e) |
| 485 | print self.rest_error_dict_to_message(error) |
| 486 | pass |
| 487 | |
| 488 | if error: |
| 489 | print "Unable to collect host-vns-interface" |
| 490 | return |
| 491 | |
| 492 | if len(host_vns_interface_table) == 0: |
| 493 | print "host_vns_interface_table empty" |
| 494 | |
| 495 | host_vns_interface_id = self.obj_keys['host-vns-interface'] |
| 496 | for host_vns_interface in host_vns_interface_table: |
| 497 | if not host_vns_interface_id in host_vns_interface: |
| 498 | print "host_vns_interface no primary key" |
| 499 | this_host_vns_interface_id = host_vns_interface[host_vns_interface_id] |
| 500 | if not 'host' in host_vns_interface: |
| 501 | print "host_vns_interface %s no host foreign key" % this_host_vns_interface_id |
| 502 | else: |
| 503 | host_foreign_key = host_vns_interface['host'] |
| 504 | if not host_foreign_key in host_ids: |
| 505 | print "host_vns_interface %s foreign key %s references missing host" % \ |
| 506 | (this_host_vns_interface_id, host_foreign_key) |
| 507 | |
| 508 | if not 'interface' in host_vns_interface: |
| 509 | print "host_vns_interface %s no vns-interface foreign key %s" % \ |
| 510 | (this_host_vns_interface_id, foreign_key) |
| 511 | else: |
| 512 | interface_foreign_key = host_vns_interface['interface'] |
| 513 | if not interface_foreign_key in vns_interface_ids: |
| 514 | print "host_vns_interface %s foreign key %s" % \ |
| 515 | (this_host_vns_interface, interface_foreign_key) |
| 516 | |
| 517 | parts = this_host_vns_interface_id.split("|") |
| 518 | if len(parts) != 3: |
| 519 | print "host_vns_interface_id %d needs to be three fields split by '|'" |
| 520 | else: |
| 521 | if parts[0] != host_foreign_key: |
| 522 | print "host_vns_interface %s related host foreign key %s isn't part of id" % \ |
| 523 | (this_host_vns_interface, host_foreign_key) |
| 524 | # the interface_foreign_key should have two parts. |
| 525 | interface_parts = interface_foreign_key.split('|') |
| 526 | if len(interface_parts) != 2: |
| 527 | print "host_vns_interface %s related vns-interface foreign key %s " \ |
| 528 | "needs to be two words split by '|'" % \ |
| 529 | (this_host_vns_interface_id, interface_foreign_key) |
| 530 | elif interface_parts[0] != parts[1]: |
| 531 | print "host_vns_interface %s related vns_interface foreign key %s " \ |
| 532 | "doesn't match host id part %s" % \ |
| 533 | (this_host_vns_interface, interface_part[0], parts[1]) |
| 534 | elif interface_parts[1] != parts[2]: |
| 535 | print "host_vns_interface %s related vns_interface foreign key %s " \ |
| 536 | "doesn't match interface long name part %s" % \ |
| 537 | (this_host_vns_interface, interface_part[1], parts[2]) |
| 538 | |
| 539 | # |
| 540 | # -------------------------------------------------------------------------------- |
| 541 | # validate_vns_interface |
| 542 | # |
| 543 | def validate_vns_interface(self): |
| 544 | print "vns-interface" |
| 545 | |
| 546 | # -------------------------------------------------------------------------------- |
| 547 | # validate_vns_interface_rule |
| 548 | # |
| 549 | # - id exists, |
| 550 | # - each row has a foreign key |
| 551 | # - each foreign key exists |
| 552 | # - the id, which is a concatenation of vns name and row id, has the correct foreign key |
| 553 | # |
| 554 | def validate_vns_interface_rule(self): |
| 555 | print "vns_interface_rule" |
| 556 | |
| 557 | error = None |
| 558 | # collect known vns's |
| 559 | try: |
| 560 | vns_table = self.store.get_table_from_store("vns-definition") |
| 561 | except Exception, e: |
| 562 | error = self.rest_error_to_dict(e) |
| 563 | pass |
| 564 | if error: |
| 565 | print "Unable to collect vns-definition" |
| 566 | return |
| 567 | |
| 568 | # collect known switches |
| 569 | switch_ids = None |
| 570 | switch_key = self.obj_keys["switch"] |
| 571 | try: |
| 572 | switch_table = self.store.get_table_from_store("switch") |
| 573 | switch_ids = [x[switch_key] for x in switch_table] |
| 574 | except Exception, e: |
| 575 | error = self.rest_error_to_dict(e) |
| 576 | print self.rest_error_dict_to_message(error) |
| 577 | pass |
| 578 | |
| 579 | if error: |
| 580 | print "Unable to collect switch, no switch dpid validation for vns rules" |
| 581 | error = None |
| 582 | |
| 583 | try: |
| 584 | vns_interface_rules_table = self.store.get_table_from_store("vns-interface-rule") |
| 585 | except Exception, e: |
| 586 | error = self.rest_error_to_dict(e) |
| 587 | pass |
| 588 | if error: |
| 589 | print "Unable to collect vns-interface-rule" |
| 590 | return |
| 591 | vns_id = self.obj_keys["vns-interface-rule"] |
| 592 | |
| 593 | for rule in vns_interface_rules_table: |
| 594 | if not vns_id in rule: |
| 595 | print "rule has missing rule id" |
| 596 | rule_id = rule[vns_id] |
| 597 | parts = rule_id.split("|") |
| 598 | if len(parts) < 2: |
| 599 | print "rule %s has invalid id" % rule_id |
| 600 | vns_part = parts[0] |
| 601 | if not 'vns' in rule: |
| 602 | print "rule %s has missing vns foreign key" % rule_id |
| 603 | else: |
| 604 | if rule['vns'] != vns_part: |
| 605 | print "rule %s has inconsistent vns foreign key: %s" % (rule_id, rule['vns']) |
| 606 | if 'ports' in rule and not 'switch' in rule: |
| 607 | print "rule %s has a ports field populated but no switch" % rule_id |
| 608 | elif 'switch' in rule and not 'ports' in rule: |
| 609 | print "rule %s has a switch field populated but no switch" % rule_id |
| 610 | if 'switch' in rule and not rule['switch'] in switch_ids: |
| 611 | print "rule %s has an unknown switch dpid %s" % (rule_id, rule['switch']) |
| 612 | |
| 613 | |
| 614 | # |
| 615 | # -------------------------------------------------------------------------------- |
| 616 | # validate |
| 617 | # |
| 618 | def validate(self): |
| 619 | print "store validation" |
| 620 | |
| 621 | tables = self.obj_type_info_dict.keys() |
| 622 | for table in tables: |
| 623 | method = self.method_from_name(table) |
| 624 | if method: |
| 625 | method() |
| 626 | |
| 627 | |
| 628 | # |
| 629 | # -------------------------------------------------------------------------------- |
| 630 | # Initialization crazyness to make it work across platforms. Many platforms don't include |
| 631 | # GNU readline (e.g. mac os x) and we need to compensate for this |
| 632 | |
| 633 | import sys |
| 634 | try: |
| 635 | import readline |
| 636 | except ImportError: |
| 637 | try: |
| 638 | import pyreadline as readline |
| 639 | except ImportError: |
| 640 | print "Can't find any readline equivalent - aborting." |
| 641 | else: |
| 642 | import rlcompleter |
| 643 | if(sys.platform == 'darwin'): |
| 644 | # needed for Mac, please fix Apple |
| 645 | readline.parse_and_bind ("bind ^I rl_complete") |
| 646 | else: |
| 647 | readline.parse_and_bind("tab: complete") |
| 648 | readline.parse_and_bind("?: possible-completions") |
| 649 | |
| 650 | |
| 651 | # |
| 652 | # -------------------------------------------------------------------------------- |
| 653 | # Run the shell |
| 654 | |
| 655 | def main(): |
| 656 | # Uncomment the next two lines to enable remote debugging with PyDev |
| 657 | #import pydevd |
| 658 | #pydevd.settrace() |
| 659 | validate = Validate() |
| 660 | validate.init() |
| 661 | validate.validate() |
| 662 | |
| 663 | if __name__ == '__main__': |
| 664 | main() |