1# Copyright (C) 2012 Nippon Telegraph and Telephone Corporation. 2# Copyright (C) 2012 Isaku Yamahata <yamahata at private email ne jp> 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 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. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17""" 18``ovs-vsctl`` command like library to speak OVSDB protocol 19""" 20 21from __future__ import print_function 22 23import logging 24import operator 25import os 26import re 27import sys 28import weakref 29 30import six 31 32import ovs.db.data 33import ovs.db.parser 34import ovs.db.schema 35import ovs.db.types 36import ovs.poller 37import ovs.json 38from ovs import jsonrpc 39from ovs import ovsuuid 40from ovs import stream 41from ovs.db import idl 42 43from ryu.lib import hub 44from ryu.lib import ip 45from ryu.lib.ovs import vswitch_idl 46from ryu.lib.stringify import StringifyMixin 47 48 49LOG = logging.getLogger(__name__) # use ovs.vlog? 50 51 52def valid_ovsdb_addr(addr): 53 """ 54 Returns True if the given addr is valid OVSDB server address, otherwise 55 False. 56 57 The valid formats are: 58 59 - ``unix:file`` 60 - ``tcp:ip:port`` 61 - ``ssl:ip:port`` 62 63 If ip is IPv6 address, wrap ip with brackets (e.g., ssl:[::1]:6640). 64 65 :param addr: str value of OVSDB server address. 66 :return: True if valid, otherwise False. 67 """ 68 # Assumes Unix socket format: "unix:file" 69 m = re.match(r'unix:(\S+)', addr) 70 if m: 71 file = m.group(1) 72 return os.path.isfile(file) 73 # Assumes TCP/SSL socket format: "tcp:ip:port" or "ssl:ip:port" 74 m = re.match(r'(tcp|ssl):(\S+):(\d+)', addr) 75 if m: 76 address = m.group(2) 77 port = m.group(3) 78 if '[' in address: 79 address = address.strip('[').strip(']') 80 return ip.valid_ipv6(address) and port.isdigit() 81 else: 82 return ip.valid_ipv4(address) and port.isdigit() 83 # Assumes invalid format or unsupported type 84 return False 85 86 87# for debug 88def ovsrec_row_changes_to_string(ovsrec_row): 89 if not ovsrec_row._changes: 90 return ovsrec_row._changes 91 92 return dict((key, value.to_string()) 93 for key, value in ovsrec_row._changes.items()) 94 95 96# for debug 97def ovsrec_row_to_string(ovsrec_row): 98 output = '' 99 output += 'uuid: %s ' % ovsrec_row.uuid 100 if ovsrec_row._data: 101 output += '_data: %s ' % dict((key, value.to_string()) for key, value 102 in ovsrec_row._data.items()) 103 else: 104 output += '_data: %s ' % ovsrec_row._data 105 output += '_changes: %s' % ovsrec_row_changes_to_string(ovsrec_row) 106 return output 107 108 109def atom_from_string(base, value_string, symtab=None): 110 type_ = base.type 111 atom = None 112 if type_ == ovs.db.types.IntegerType: 113 atom = ovs.db.data.Atom(type_, int(value_string)) 114 elif type_ == ovs.db.types.RealType: 115 # TODO:XXX negation 116 atom = ovs.db.data.Atom( 117 type_, ovs.db.parser.float_to_int(float(value_string))) 118 elif type_ == ovs.db.types.BooleanType: 119 if value_string in ("true", "yes", "on", "1"): 120 atom = ovs.db.data.Atom(type_, True) 121 elif value_string == ("false", "no", "off", "0"): 122 atom = ovs.db.data.Atom(type_, False) 123 elif type_ == ovs.db.types.StringType: 124 # TODO:XXXX escape: if value_string[0] == '"': 125 atom = ovs.db.data.Atom(type_, value_string) 126 elif type_ == ovs.db.types.UuidType: 127 if value_string[0] == "@": 128 assert symtab is not None 129 uuid_ = symtab[value_string] 130 atom = ovs.db.data.Atom(type_, uuid_) 131 else: 132 atom = ovs.db.data.Atom(type_, 133 ovs.ovsuuid.from_string(value_string)) 134 if atom is None: 135 raise ValueError("expected %s" % type_.to_string(), value_string) 136 atom.check_constraints(base) 137 return atom 138 139 140def datum_from_string(type_, value_string, symtab=None): 141 value_string = value_string.strip() 142 if type_.is_map(): 143 if value_string.startswith('{'): 144 # TODO:dict case 145 LOG.debug('value_string %s', value_string) 146 raise NotImplementedError() 147 d = dict(v.split('=', 1) for v in value_string.split(',')) 148 d = dict((atom_from_string(type_.key, key, symtab), 149 atom_from_string(type_.value, value, symtab)) 150 for key, value in d.items()) 151 elif type_.is_set(): 152 if value_string.startswith('['): 153 # TODO:set case 154 LOG.debug('value_string %s', value_string) 155 raise NotImplementedError() 156 values = value_string.split(',') 157 d = dict((atom_from_string(type_.key, value, symtab), None) 158 for value in values) 159 else: 160 atom = atom_from_string(type_.key, value_string, symtab) 161 d = {atom: None} 162 163 datum = ovs.db.data.Datum(type_, d) 164 return datum.to_json() 165 166 167def ifind(pred, seq): 168 try: 169 return [i for i in seq if pred(i)][0] 170 except IndexError: 171 return None 172 173 174def not_reached(): 175 os.abort() 176 177 178def vsctl_fatal(msg): 179 LOG.error(msg) 180 raise Exception(msg) # not call ovs.utils.ovs_fatal for reusability 181 182 183class VSCtlBridge(object): 184 185 def __init__(self, ovsrec_bridge, name, parent, vlan): 186 super(VSCtlBridge, self).__init__() 187 self.br_cfg = ovsrec_bridge 188 self.name = name 189 self.ports = set() 190 self.parent = parent 191 self.vlan = vlan 192 self.children = set() # WeakSet is needed? 193 194 def find_vlan_bridge(self, vlan): 195 return ifind(lambda child: child.vlan == vlan, self.children) 196 197 198class VSCtlPort(object): 199 200 def __init__(self, vsctl_bridge_parent, ovsrec_port): 201 super(VSCtlPort, self).__init__() 202 self.bridge = weakref.ref(vsctl_bridge_parent) # backpointer 203 self.port_cfg = ovsrec_port 204 205 self.ifaces = set() 206 self.qos = None 207 208 209class VSCtlIface(object): 210 211 def __init__(self, vsctl_port_parent, ovsrec_iface): 212 super(VSCtlIface, self).__init__() 213 self.port = weakref.ref(vsctl_port_parent) # backpointer 214 self.iface_cfg = ovsrec_iface 215 216 217class VSCtlQoS(object): 218 219 def __init__(self, vsctl_port_parent, ovsrec_qos): 220 super(VSCtlQoS, self).__init__() 221 self.port = weakref.ref(vsctl_port_parent) 222 self.qos_cfg = ovsrec_qos 223 self.queues = set() 224 225 226class VSCtlQueue(object): 227 228 def __init__(self, vsctl_qos_parent, ovsrec_queue): 229 super(VSCtlQueue, self).__init__() 230 self.qos = weakref.ref(vsctl_qos_parent) 231 self.queue_cfg = ovsrec_queue 232 233 234class VSCtlContext(object): 235 236 def _invalidate_cache(self): 237 self.cache_valid = False 238 self.bridges.clear() 239 self.ports.clear() 240 self.ifaces.clear() 241 242 def __init__(self, idl_, txn, ovsrec_open_vswitch): 243 super(VSCtlContext, self).__init__() 244 245 # Modifiable state 246 # self.table = None 247 self.idl = idl_ 248 self.txn = txn 249 self.ovs = ovsrec_open_vswitch 250 self.symtab = None # TODO:XXX 251 self.verified_ports = False 252 253 # A cache of the contents of the database. 254 self.cache_valid = False 255 self.bridges = {} # bridge name -> VSCtlBridge 256 self.ports = {} # port name -> VSCtlPort 257 self.ifaces = {} # iface name -> VSCtlIface 258 259 self.try_again = False # used by wait-until command 260 261 def done(self): 262 self._invalidate_cache() 263 264 def verify_bridges(self): 265 self.ovs.verify(vswitch_idl.OVSREC_OPEN_VSWITCH_COL_BRIDGES) 266 267 def verify_ports(self): 268 if self.verified_ports: 269 return 270 271 self.verify_bridges() 272 for ovsrec_bridge in self.idl.tables[ 273 vswitch_idl.OVSREC_TABLE_BRIDGE].rows.values(): 274 ovsrec_bridge.verify(vswitch_idl.OVSREC_BRIDGE_COL_PORTS) 275 for ovsrec_port in self.idl.tables[ 276 vswitch_idl.OVSREC_TABLE_PORT].rows.values(): 277 ovsrec_port.verify(vswitch_idl.OVSREC_PORT_COL_INTERFACES) 278 self.verified_ports = True 279 280 def add_bridge_to_cache(self, ovsrec_bridge, name, parent, vlan): 281 vsctl_bridge = VSCtlBridge(ovsrec_bridge, name, parent, vlan) 282 if parent: 283 parent.children.add(vsctl_bridge) 284 self.bridges[name] = vsctl_bridge 285 return vsctl_bridge 286 287 def del_cached_bridge(self, vsctl_bridge): 288 assert not vsctl_bridge.ports 289 assert not vsctl_bridge.children 290 291 parent = vsctl_bridge.parent 292 if parent: 293 parent.children.remove(vsctl_bridge) 294 vsctl_bridge.parent = None # break circular reference 295 ovsrec_bridge = vsctl_bridge.br_cfg 296 if ovsrec_bridge: 297 ovsrec_bridge.delete() 298 self.ovs_delete_bridge(ovsrec_bridge) 299 300 del self.bridges[vsctl_bridge.name] 301 302 def del_cached_qos(self, vsctl_qos): 303 vsctl_qos.port().qos = None 304 vsctl_qos.port = None 305 vsctl_qos.queues = None 306 307 def add_port_to_cache(self, vsctl_bridge_parent, ovsrec_port): 308 tag = getattr(ovsrec_port, vswitch_idl.OVSREC_PORT_COL_TAG, None) 309 if isinstance(tag, list): 310 if len(tag) == 0: 311 tag = 0 312 else: 313 tag = tag[0] 314 if tag is not None and 0 <= tag < 4096: 315 vlan_bridge = vsctl_bridge_parent.find_vlan_bridge(tag) 316 if vlan_bridge: 317 vsctl_bridge_parent = vlan_bridge 318 319 vsctl_port = VSCtlPort(vsctl_bridge_parent, ovsrec_port) 320 vsctl_bridge_parent.ports.add(vsctl_port) 321 self.ports[ovsrec_port.name] = vsctl_port 322 return vsctl_port 323 324 def del_cached_port(self, vsctl_port): 325 assert not vsctl_port.ifaces 326 vsctl_port.bridge().ports.remove(vsctl_port) 327 vsctl_port.bridge = None 328 port = self.ports.pop(vsctl_port.port_cfg.name) 329 assert port == vsctl_port 330 vsctl_port.port_cfg.delete() 331 332 def add_iface_to_cache(self, vsctl_port_parent, ovsrec_iface): 333 vsctl_iface = VSCtlIface(vsctl_port_parent, ovsrec_iface) 334 vsctl_port_parent.ifaces.add(vsctl_iface) 335 self.ifaces[ovsrec_iface.name] = vsctl_iface 336 337 def add_qos_to_cache(self, vsctl_port_parent, ovsrec_qos): 338 vsctl_qos = VSCtlQoS(vsctl_port_parent, ovsrec_qos) 339 vsctl_port_parent.qos = vsctl_qos 340 return vsctl_qos 341 342 def add_queue_to_cache(self, vsctl_qos_parent, ovsrec_queue): 343 vsctl_queue = VSCtlQueue(vsctl_qos_parent, ovsrec_queue) 344 vsctl_qos_parent.queues.add(vsctl_queue) 345 346 def del_cached_iface(self, vsctl_iface): 347 vsctl_iface.port().ifaces.remove(vsctl_iface) 348 vsctl_iface.port = None 349 del self.ifaces[vsctl_iface.iface_cfg.name] 350 vsctl_iface.iface_cfg.delete() 351 352 def invalidate_cache(self): 353 if not self.cache_valid: 354 return 355 self._invalidate_cache() 356 357 def populate_cache(self): 358 self._populate_cache(self.idl.tables[vswitch_idl.OVSREC_TABLE_BRIDGE]) 359 360 @staticmethod 361 def port_is_fake_bridge(ovsrec_port): 362 tag = ovsrec_port.tag 363 if isinstance(tag, list): 364 if len(tag) == 0: 365 tag = 0 366 else: 367 tag = tag[0] 368 return ovsrec_port.fake_bridge and 0 <= tag <= 4095 369 370 def _populate_cache(self, ovsrec_bridges): 371 if self.cache_valid: 372 return 373 self.cache_valid = True 374 375 bridges = set() 376 ports = set() 377 for ovsrec_bridge in ovsrec_bridges.rows.values(): 378 name = ovsrec_bridge.name 379 if name in bridges: 380 LOG.warning('%s: database contains duplicate bridge name', 381 name) 382 bridges.add(name) 383 vsctl_bridge = self.add_bridge_to_cache(ovsrec_bridge, name, 384 None, 0) 385 if not vsctl_bridge: 386 continue 387 for ovsrec_port in ovsrec_bridge.ports: 388 port_name = ovsrec_port.name 389 if port_name in ports: 390 # Duplicate ovsrec_port name. 391 # (We will warn about that later.) 392 continue 393 ports.add(port_name) 394 if (self.port_is_fake_bridge(ovsrec_port) and 395 port_name not in bridges): 396 bridges.add(port_name) 397 self.add_bridge_to_cache(None, port_name, vsctl_bridge, 398 ovsrec_port.tag) 399 400 bridges = set() 401 for ovsrec_bridge in ovsrec_bridges.rows.values(): 402 name = ovsrec_bridge.name 403 if name in bridges: 404 continue 405 bridges.add(name) 406 vsctl_bridge = self.bridges[name] 407 for ovsrec_port in ovsrec_bridge.ports: 408 port_name = ovsrec_port.name 409 vsctl_port = self.ports.get(port_name) 410 if vsctl_port: 411 if ovsrec_port == vsctl_port.port_cfg: 412 LOG.warning('%s: vsctl_port is in multiple bridges ' 413 '(%s and %s)', 414 port_name, vsctl_bridge.name, 415 vsctl_port.br.name) 416 else: 417 LOG.error('%s: database contains duplicate ' 418 'vsctl_port name', 419 ovsrec_port.name) 420 continue 421 422 if (self.port_is_fake_bridge(ovsrec_port) and 423 port_name in bridges): 424 continue 425 426 # LOG.debug('ovsrec_port %s %s %s', 427 # ovsrec_port, ovsrec_port._data, ovsrec_port.tag) 428 vsctl_port = self.add_port_to_cache(vsctl_bridge, ovsrec_port) 429 # LOG.debug('vsctl_port %s', vsctl_port) 430 for ovsrec_iface in ovsrec_port.interfaces: 431 iface = self.ifaces.get(ovsrec_iface.name) 432 if iface: 433 if ovsrec_iface == iface.iface_cfg: 434 LOG.warning( 435 '%s: interface is in multiple ports ' 436 '(%s and %s)', 437 ovsrec_iface.name, 438 iface.port().port_cfg.name, 439 vsctl_port.port_cfg.name) 440 else: 441 LOG.error( 442 '%s: database contains duplicate interface ' 443 'name', 444 ovsrec_iface.name) 445 continue 446 self.add_iface_to_cache(vsctl_port, ovsrec_iface) 447 ovsrec_qos = ovsrec_port.qos 448 vsctl_qos = self.add_qos_to_cache(vsctl_port, ovsrec_qos) 449 if len(ovsrec_qos): 450 for ovsrec_queue in ovsrec_qos[0].queues: 451 self.add_queue_to_cache(vsctl_qos, ovsrec_queue) 452 453 def check_conflicts(self, name, msg): 454 self.verify_ports() 455 if name in self.bridges: 456 vsctl_fatal('%s because a bridge named %s already exists' % 457 (msg, name)) 458 if name in self.ports: 459 vsctl_fatal('%s because a port named %s already exists on ' 460 'bridge %s' % 461 (msg, name, self.ports[name].bridge().name)) 462 if name in self.ifaces: 463 vsctl_fatal('%s because an interface named %s already ' 464 'exists on bridge %s' % 465 (msg, name, self.ifaces[name].port().bridge().name)) 466 467 def find_bridge(self, name, must_exist): 468 assert self.cache_valid 469 vsctl_bridge = self.bridges.get(name) 470 if must_exist and not vsctl_bridge: 471 vsctl_fatal('no bridge named %s' % name) 472 self.verify_bridges() 473 return vsctl_bridge 474 475 def find_real_bridge(self, name, must_exist): 476 vsctl_bridge = self.find_bridge(name, must_exist) 477 if vsctl_bridge and vsctl_bridge.parent: 478 vsctl_fatal('%s is a fake bridge' % name) 479 return vsctl_bridge 480 481 def find_bridge_by_id(self, datapath_id, must_exist): 482 assert self.cache_valid 483 for vsctl_bridge in self.bridges.values(): 484 if vsctl_bridge.br_cfg.datapath_id[0].strip('"') == datapath_id: 485 self.verify_bridges() 486 return vsctl_bridge 487 488 if must_exist: 489 vsctl_fatal('no bridge id %s' % datapath_id) 490 return None 491 492 def find_port(self, name, must_exist): 493 assert self.cache_valid 494 vsctl_port = self.ports.get(name) 495 if vsctl_port and name == vsctl_port.bridge().name: 496 vsctl_port = None 497 if must_exist and not vsctl_port: 498 vsctl_fatal('no vsctl_port named %s' % name) 499 return vsctl_port 500 501 def find_iface(self, name, must_exist): 502 assert self.cache_valid 503 vsctl_iface = self.ifaces.get(name) 504 if vsctl_iface and name == vsctl_iface.port().bridge().name: 505 vsctl_iface = None 506 if must_exist and not vsctl_iface: 507 vsctl_fatal('no interface named %s' % name) 508 self.verify_ports() 509 return vsctl_iface 510 511 def set_qos(self, vsctl_port, type, max_rate): 512 qos = vsctl_port.qos.qos_cfg 513 if not len(qos): 514 ovsrec_qos = self.txn.insert( 515 self.txn.idl.tables[vswitch_idl.OVSREC_TABLE_QOS]) 516 vsctl_port.port_cfg.qos = [ovsrec_qos] 517 else: 518 ovsrec_qos = qos[0] 519 ovsrec_qos.type = type 520 if max_rate is not None: 521 value_json = ['map', [['max-rate', max_rate]]] 522 self.set_column(ovsrec_qos, 'other_config', value_json) 523 self.add_qos_to_cache(vsctl_port, [ovsrec_qos]) 524 return ovsrec_qos 525 526 def set_queue(self, vsctl_qos, max_rate, min_rate, 527 queue_id): 528 529 ovsrec_qos = vsctl_qos.qos_cfg[0] 530 try: 531 ovsrec_queue = ovsrec_qos.queues[queue_id] 532 except (AttributeError, KeyError): 533 ovsrec_queue = self.txn.insert( 534 self.txn.idl.tables[vswitch_idl.OVSREC_TABLE_QUEUE]) 535 if max_rate is not None: 536 value_json = ['map', [['max-rate', max_rate]]] 537 self.add_column(ovsrec_queue, 'other_config', value_json) 538 if min_rate is not None: 539 value_json = ['map', [['min-rate', min_rate]]] 540 self.add_column(ovsrec_queue, 'other_config', value_json) 541 value_json = ['map', [[queue_id, ['uuid', str(ovsrec_queue.uuid)]]]] 542 self.add_column(ovsrec_qos, 'queues', value_json) 543 self.add_queue_to_cache(vsctl_qos, ovsrec_queue) 544 return ovsrec_queue 545 546 @staticmethod 547 def _column_set(ovsrec_row, column, ovsrec_value): 548 # need to trigger Row.__setattr__() 549 setattr(ovsrec_row, column, ovsrec_value) 550 551 @staticmethod 552 def _column_insert(ovsrec_row, column, ovsrec_add): 553 value = getattr(ovsrec_row, column) 554 value.append(ovsrec_add) 555 VSCtlContext._column_set(ovsrec_row, column, value) 556 557 @staticmethod 558 def _column_delete(ovsrec_row, column, ovsrec_del): 559 value = getattr(ovsrec_row, column) 560 try: 561 value.remove(ovsrec_del) 562 except ValueError: 563 # Datum.to_python() with _uuid_to_row trims down deleted 564 # references. If ovsrec_del.delete() is called before 565 # _column_delete(), value doesn't include ovsrec_del. 566 pass 567 568 VSCtlContext._column_set(ovsrec_row, column, value) 569 570 @staticmethod 571 def bridge_insert_port(ovsrec_bridge, ovsrec_port): 572 VSCtlContext._column_insert(ovsrec_bridge, 573 vswitch_idl.OVSREC_BRIDGE_COL_PORTS, 574 ovsrec_port) 575 576 @staticmethod 577 def bridge_delete_port(ovsrec_bridge, ovsrec_port): 578 VSCtlContext._column_delete(ovsrec_bridge, 579 vswitch_idl.OVSREC_BRIDGE_COL_PORTS, 580 ovsrec_port) 581 582 @staticmethod 583 def port_delete_qos(ovsrec_port, ovsrec_qos): 584 VSCtlContext._column_delete(ovsrec_port, 585 vswitch_idl.OVSREC_PORT_COL_QOS, 586 ovsrec_qos) 587 588 def ovs_insert_bridge(self, ovsrec_bridge): 589 self._column_insert(self.ovs, 590 vswitch_idl.OVSREC_OPEN_VSWITCH_COL_BRIDGES, 591 ovsrec_bridge) 592 593 def ovs_delete_bridge(self, ovsrec_bridge): 594 self._column_delete(self.ovs, 595 vswitch_idl.OVSREC_OPEN_VSWITCH_COL_BRIDGES, 596 ovsrec_bridge) 597 598 def del_port(self, vsctl_port): 599 if vsctl_port.bridge().parent: 600 ovsrec_bridge = vsctl_port.bridge().parent.br_cfg 601 else: 602 ovsrec_bridge = vsctl_port.bridge().br_cfg 603 self.bridge_delete_port(ovsrec_bridge, vsctl_port.port_cfg) 604 605 for vsctl_iface in vsctl_port.ifaces.copy(): 606 self.del_cached_iface(vsctl_iface) 607 self.del_cached_port(vsctl_port) 608 609 def del_bridge(self, vsctl_bridge): 610 for child in vsctl_bridge.children.copy(): 611 self.del_bridge(child) 612 for vsctl_port in vsctl_bridge.ports.copy(): 613 self.del_port(vsctl_port) 614 self.del_cached_bridge(vsctl_bridge) 615 616 def del_qos(self, vsctl_qos): 617 ovsrec_port = vsctl_qos.port().port_cfg 618 ovsrec_qos = vsctl_qos.qos_cfg 619 if len(ovsrec_qos): 620 self.port_delete_qos(ovsrec_port, ovsrec_qos[0]) 621 self.del_cached_qos(vsctl_qos) 622 623 def add_port(self, br_name, port_name, may_exist, fake_iface, 624 iface_names, settings=None): 625 """ 626 :type settings: list of (column, value_json) 627 where column is str, 628 value_json is json that is represented 629 by Datum.to_json() 630 """ 631 settings = settings or [] 632 633 self.populate_cache() 634 if may_exist: 635 vsctl_port = self.find_port(port_name, False) 636 if vsctl_port: 637 want_names = set(iface_names) 638 have_names = set(ovsrec_iface.name for ovsrec_iface in 639 vsctl_port.port_cfg.interfaces) 640 if vsctl_port.bridge().name != br_name: 641 vsctl_fatal('"%s" but %s is actually attached to ' 642 'vsctl_bridge %s' % 643 (br_name, port_name, vsctl_port.bridge().name)) 644 if want_names != have_names: 645 want_names_string = ','.join(want_names) 646 have_names_string = ','.join(have_names) 647 vsctl_fatal('"%s" but %s actually has interface(s) %s' % 648 (want_names_string, 649 port_name, have_names_string)) 650 return 651 self.check_conflicts(port_name, 652 'cannot create a port named %s' % port_name) 653 for iface_name in iface_names: 654 self.check_conflicts( 655 iface_name, 'cannot create an interface named %s' % iface_name) 656 657 vsctl_bridge = self.find_bridge(br_name, True) 658 ifaces = [] 659 for iface_name in iface_names: 660 ovsrec_iface = self.txn.insert( 661 self.idl.tables[vswitch_idl.OVSREC_TABLE_INTERFACE]) 662 ovsrec_iface.name = iface_name 663 ifaces.append(ovsrec_iface) 664 665 ovsrec_port = self.txn.insert( 666 self.idl.tables[vswitch_idl.OVSREC_TABLE_PORT]) 667 ovsrec_port.name = port_name 668 ovsrec_port.interfaces = ifaces 669 ovsrec_port.bond_fake_iface = fake_iface 670 671 if vsctl_bridge.parent: 672 tag = vsctl_bridge.vlan 673 ovsrec_port.tag = tag 674 for column, value in settings: 675 # TODO:XXX self.symtab: 676 self.set_column(ovsrec_port, column, value) 677 678 if vsctl_bridge.parent: 679 ovsrec_bridge = vsctl_bridge.parent.br_cfg 680 else: 681 ovsrec_bridge = vsctl_bridge.br_cfg 682 self.bridge_insert_port(ovsrec_bridge, ovsrec_port) 683 vsctl_port = self.add_port_to_cache(vsctl_bridge, ovsrec_port) 684 for ovsrec_iface in ifaces: 685 self.add_iface_to_cache(vsctl_port, ovsrec_iface) 686 687 def add_bridge(self, br_name, parent_name=None, vlan=0, may_exist=False): 688 self.populate_cache() 689 if may_exist: 690 vsctl_bridge = self.find_bridge(br_name, False) 691 if vsctl_bridge: 692 if not parent_name: 693 if vsctl_bridge.parent: 694 vsctl_fatal('"--may-exist add-vsctl_bridge %s" ' 695 'but %s is a VLAN bridge for VLAN %d' % 696 (br_name, br_name, vsctl_bridge.vlan)) 697 else: 698 if not vsctl_bridge.parent: 699 vsctl_fatal('"--may-exist add-vsctl_bridge %s %s %d" ' 700 'but %s is not a VLAN bridge' % 701 (br_name, parent_name, vlan, br_name)) 702 elif vsctl_bridge.parent.name != parent_name: 703 vsctl_fatal('"--may-exist add-vsctl_bridge %s %s %d" ' 704 'but %s has the wrong parent %s' % 705 (br_name, parent_name, vlan, 706 br_name, vsctl_bridge.parent.name)) 707 elif vsctl_bridge.vlan != vlan: 708 vsctl_fatal('"--may-exist add-vsctl_bridge %s %s %d" ' 709 'but %s is a VLAN bridge for the wrong ' 710 'VLAN %d' % 711 (br_name, parent_name, vlan, br_name, 712 vsctl_bridge.vlan)) 713 return 714 715 self.check_conflicts(br_name, 716 'cannot create a bridge named %s' % br_name) 717 718 txn = self.txn 719 tables = self.idl.tables 720 if not parent_name: 721 ovsrec_iface = txn.insert( 722 tables[vswitch_idl.OVSREC_TABLE_INTERFACE]) 723 ovsrec_iface.name = br_name 724 ovsrec_iface.type = 'internal' 725 726 ovsrec_port = txn.insert(tables[vswitch_idl.OVSREC_TABLE_PORT]) 727 ovsrec_port.name = br_name 728 ovsrec_port.interfaces = [ovsrec_iface] 729 ovsrec_port.fake_bridge = False 730 731 ovsrec_bridge = txn.insert(tables[vswitch_idl.OVSREC_TABLE_BRIDGE]) 732 ovsrec_bridge.name = br_name 733 ovsrec_bridge.ports = [ovsrec_port] 734 735 self.ovs_insert_bridge(ovsrec_bridge) 736 else: 737 parent = self.find_bridge(parent_name, False) 738 if parent and parent.parent: 739 vsctl_fatal('cannot create bridge with fake bridge as parent') 740 if not parent: 741 vsctl_fatal('parent bridge %s does not exist' % parent_name) 742 743 ovsrec_iface = txn.insert( 744 tables[vswitch_idl.OVSREC_TABLE_INTERFACE]) 745 ovsrec_iface.name = br_name 746 ovsrec_iface.type = 'internal' 747 748 ovsrec_port = txn.insert(tables[vswitch_idl.OVSREC_TABLE_PORT]) 749 ovsrec_port.name = br_name 750 ovsrec_port.interfaces = [ovsrec_iface] 751 ovsrec_port.fake_bridge = True 752 ovsrec_port.tag = vlan 753 754 self.bridge_insert_port(parent.br_cfg, ovsrec_port) 755 756 self.invalidate_cache() 757 758 @staticmethod 759 def parse_column_key(setting_string): 760 """ 761 Parses 'setting_string' as str formatted in <column>[:<key>] 762 and returns str type 'column' and 'key' 763 """ 764 if ':' in setting_string: 765 # splits <column>:<key> into <column> and <key> 766 column, key = setting_string.split(':', 1) 767 else: 768 # stores <column> and <value>=None 769 column = setting_string 770 key = None 771 772 return column, key 773 774 @staticmethod 775 def parse_column_key_value(table_schema, setting_string): 776 """ 777 Parses 'setting_string' as str formatted in <column>[:<key>]=<value> 778 and returns str type 'column' and json formatted 'value' 779 """ 780 if ':' in setting_string: 781 # splits <column>:<key>=<value> into <column> and <key>=<value> 782 column, value = setting_string.split(':', 1) 783 elif '=' in setting_string: 784 # splits <column>=<value> into <column> and <value> 785 column, value = setting_string.split('=', 1) 786 else: 787 # stores <column> and <value>=None 788 column = setting_string 789 value = None 790 791 if value is not None: 792 type_ = table_schema.columns[column].type 793 value = datum_from_string(type_, value) 794 795 return column, value 796 797 def get_column(self, ovsrec_row, column, key=None, if_exists=False): 798 value = getattr(ovsrec_row, column, None) 799 if isinstance(value, dict) and key is not None: 800 value = value.get(key, None) 801 column = '%s:%s' % (column, key) 802 803 if value is None: 804 if if_exists: 805 return None 806 vsctl_fatal('%s does not contain a column whose name matches "%s"' 807 % (ovsrec_row._table.name, column)) 808 809 return value 810 811 def _pre_mod_column(self, ovsrec_row, column, value_json): 812 if column not in ovsrec_row._table.columns: 813 vsctl_fatal('%s does not contain a column whose name matches "%s"' 814 % (ovsrec_row._table.name, column)) 815 816 column_schema = ovsrec_row._table.columns[column] 817 datum = ovs.db.data.Datum.from_json( 818 column_schema.type, value_json, self.symtab) 819 return datum.to_python(ovs.db.idl._uuid_to_row) 820 821 def set_column(self, ovsrec_row, column, value_json): 822 column_schema = ovsrec_row._table.columns[column] 823 datum = self._pre_mod_column(ovsrec_row, column, value_json) 824 825 if column_schema.type.is_map(): 826 values = getattr(ovsrec_row, column, {}) 827 values.update(datum) 828 else: 829 values = datum 830 831 setattr(ovsrec_row, column, values) 832 833 def add_column(self, ovsrec_row, column, value_json): 834 column_schema = ovsrec_row._table.columns[column] 835 datum = self._pre_mod_column(ovsrec_row, column, value_json) 836 837 if column_schema.type.is_map(): 838 values = getattr(ovsrec_row, column, {}) 839 values.update(datum) 840 elif column_schema.type.is_set(): 841 values = getattr(ovsrec_row, column, []) 842 values.extend(datum) 843 else: 844 values = datum 845 846 setattr(ovsrec_row, column, values) 847 848 def remove_column(self, ovsrec_row, column, value_json): 849 column_schema = ovsrec_row._table.columns[column] 850 datum = self._pre_mod_column(ovsrec_row, column, value_json) 851 852 if column_schema.type.is_map(): 853 values = getattr(ovsrec_row, column, {}) 854 for datum_key, datum_value in datum.items(): 855 v = values.get(datum_key, None) 856 if v == datum_value: 857 values.pop(datum_key) 858 setattr(ovsrec_row, column, values) 859 elif column_schema.type.is_set(): 860 values = getattr(ovsrec_row, column, []) 861 for d in datum: 862 if d in values: 863 values.remove(d) 864 setattr(ovsrec_row, column, values) 865 else: 866 values = getattr(ovsrec_row, column, None) 867 default = ovs.db.data.Datum.default(column_schema.type) 868 default = default.to_python(ovs.db.idl._uuid_to_row).to_json() 869 if values == datum: 870 setattr(ovsrec_row, column, default) 871 872 def _get_row_by_id(self, table_name, vsctl_row_id, record_id): 873 if not vsctl_row_id.table: 874 return None 875 876 if not vsctl_row_id.name_column: 877 if record_id != '.': 878 return None 879 values = list(self.idl.tables[vsctl_row_id.table].rows.values()) 880 if not values or len(values) > 2: 881 return None 882 referrer = values[0] 883 else: 884 referrer = None 885 for ovsrec_row in self.idl.tables[ 886 vsctl_row_id.table].rows.values(): 887 name = getattr(ovsrec_row, vsctl_row_id.name_column) 888 assert isinstance(name, (list, str, six.text_type)) 889 if not isinstance(name, list) and name == record_id: 890 if referrer: 891 vsctl_fatal('multiple rows in %s match "%s"' % 892 (table_name, record_id)) 893 referrer = ovsrec_row 894 895 if not referrer: 896 return None 897 898 final = None 899 if vsctl_row_id.uuid_column: 900 referrer.verify(vsctl_row_id.uuid_column) 901 uuid = getattr(referrer, vsctl_row_id.uuid_column) 902 903 uuid_ = referrer._data[vsctl_row_id.uuid_column] 904 assert uuid_.type.key.type == ovs.db.types.UuidType 905 assert uuid_.type.value is None 906 assert isinstance(uuid, list) 907 908 if len(uuid) == 1: 909 final = uuid[0] 910 else: 911 final = referrer 912 913 return final 914 915 def get_row(self, vsctl_table, record_id): 916 table_name = vsctl_table.table_name 917 if ovsuuid.is_valid_string(record_id): 918 uuid = ovsuuid.from_string(record_id) 919 return self.idl.tables[table_name].rows.get(uuid) 920 else: 921 for vsctl_row_id in vsctl_table.row_ids: 922 ovsrec_row = self._get_row_by_id(table_name, vsctl_row_id, 923 record_id) 924 if ovsrec_row: 925 return ovsrec_row 926 927 return None 928 929 def must_get_row(self, vsctl_table, record_id): 930 ovsrec_row = self.get_row(vsctl_table, record_id) 931 if not ovsrec_row: 932 vsctl_fatal('no row "%s" in table %s' % (record_id, 933 vsctl_table.table_name)) 934 return ovsrec_row 935 936 937class _CmdShowTable(object): 938 939 def __init__(self, table, name_column, columns, recurse): 940 super(_CmdShowTable, self).__init__() 941 self.table = table 942 self.name_column = name_column 943 self.columns = columns 944 self.recurse = recurse 945 946 947class _VSCtlRowID(object): 948 949 def __init__(self, table, name_column, uuid_column): 950 super(_VSCtlRowID, self).__init__() 951 self.table = table 952 self.name_column = name_column 953 self.uuid_column = uuid_column 954 955 956class _VSCtlTable(object): 957 958 def __init__(self, table_name, vsctl_row_id_list): 959 super(_VSCtlTable, self).__init__() 960 self.table_name = table_name 961 self.row_ids = vsctl_row_id_list 962 963 964class VSCtlCommand(StringifyMixin): 965 """ 966 Class to describe artgumens similar to those of ``ovs-vsctl`` command. 967 968 ``command`` specifies the command of ``ovs-vsctl``. 969 970 ``args`` specifies a list or tuple of arguments for the given command. 971 972 ``options`` specifies a list or tuple of options for the given command. 973 Please note that NOT all options of ``ovs-vsctl`` are supported. 974 For example, ``--id`` option is not yet supported. 975 This class supports the followings. 976 977 ================= ========================================================= 978 Option Description 979 ================= ========================================================= 980 ``--may-exist`` Does nothing when the given port already exists. 981 The supported commands are ``add-port`` and 982 ``add-bond``. 983 ``--fake-iface`` Creates a port as a fake interface. 984 The supported command is ``add-bond``. 985 ``--must-exist`` Raises exception if the given port does not exist. 986 The supported command is ``del-port``. 987 ``--with-iface`` Takes effect to the interface which has the same name. 988 The supported command is ``del-port``. 989 ``--if-exists`` Ignores exception when not found. 990 The supported command is ``get``. 991 ================= ========================================================= 992 """ 993 994 def __init__(self, command, args=None, options=None): 995 super(VSCtlCommand, self).__init__() 996 self.command = command 997 self.args = args or [] 998 self.options = options or [] 999 1000 # Data modified by commands 1001 self.result = None 1002 1003 # internally used by VSCtl 1004 self._prerequisite = None 1005 self._run = None 1006 1007 def has_option(self, option): 1008 return option in self.options 1009 1010 1011class VSCtl(object): 1012 """ 1013 A class to describe an Open vSwitch instance. 1014 1015 ``remote`` specifies the address of the OVS instance. 1016 :py:mod:`ryu.lib.ovs.vsctl.valid_ovsdb_addr` is a convenient function to 1017 validate this address. 1018 """ 1019 1020 def _reset(self): 1021 self.schema_helper = None 1022 self.ovs = None 1023 self.txn = None 1024 self.wait_for_reload = True 1025 self.dry_run = False 1026 1027 def __init__(self, remote): 1028 super(VSCtl, self).__init__() 1029 self.remote = remote 1030 1031 self.schema_json = None 1032 self.schema = None 1033 self.schema_helper = None 1034 self.ovs = None 1035 self.txn = None 1036 self.wait_for_reload = True 1037 self.dry_run = False 1038 1039 def _rpc_get_schema_json(self, database): 1040 LOG.debug('remote %s', self.remote) 1041 error, stream_ = stream.Stream.open_block( 1042 stream.Stream.open(self.remote)) 1043 if error: 1044 vsctl_fatal('error %s' % os.strerror(error)) 1045 rpc = jsonrpc.Connection(stream_) 1046 request = jsonrpc.Message.create_request('get_schema', [database]) 1047 error, reply = rpc.transact_block(request) 1048 rpc.close() 1049 1050 if error: 1051 vsctl_fatal(os.strerror(error)) 1052 elif reply.error: 1053 vsctl_fatal('error %s' % reply.error) 1054 return reply.result 1055 1056 def _init_schema_helper(self): 1057 if self.schema_json is None: 1058 self.schema_json = self._rpc_get_schema_json( 1059 vswitch_idl.OVSREC_DB_NAME) 1060 schema_helper = idl.SchemaHelper(None, self.schema_json) 1061 schema_helper.register_all() 1062 self.schema = schema_helper.get_idl_schema() 1063 # LOG.debug('schema_json %s', schema_json) 1064 self.schema_helper = idl.SchemaHelper(None, self.schema_json) 1065 1066 @staticmethod 1067 def _idl_block(idl_): 1068 poller = ovs.poller.Poller() 1069 idl_.wait(poller) 1070 poller.block() 1071 1072 @staticmethod 1073 def _idl_wait(idl_, seqno): 1074 while idl_.change_seqno == seqno and not idl_.run(): 1075 VSCtl._idl_block(idl_) 1076 1077 def _run_prerequisites(self, commands): 1078 schema_helper = self.schema_helper 1079 schema_helper.register_table(vswitch_idl.OVSREC_TABLE_OPEN_VSWITCH) 1080 if self.wait_for_reload: 1081 # LOG.debug('schema_helper._tables %s', schema_helper._tables) 1082 schema_helper.register_columns( 1083 vswitch_idl.OVSREC_TABLE_OPEN_VSWITCH, 1084 [vswitch_idl.OVSREC_OPEN_VSWITCH_COL_CUR_CFG]) 1085 1086 for command in commands: 1087 if not command._prerequisite: 1088 continue 1089 ctx = VSCtlContext(None, None, None) 1090 command._prerequisite(ctx, command) 1091 ctx.done() 1092 1093 def _do_vsctl(self, idl_, commands): 1094 self.txn = idl.Transaction(idl_) 1095 if self.dry_run: 1096 self.txn.dry_run = True 1097 1098 self.txn.add_comment('ovs-vsctl') # TODO:XXX add operation name. args 1099 ovs_rows = idl_.tables[vswitch_idl.OVSREC_TABLE_OPEN_VSWITCH].rows 1100 if ovs_rows: 1101 ovs_ = list(ovs_rows.values())[0] 1102 else: 1103 # XXX add verification that table is empty 1104 ovs_ = self.txn.insert( 1105 idl_.tables[vswitch_idl.OVSREC_TABLE_OPEN_VSWITCH]) 1106 1107 if self.wait_for_reload: 1108 ovs_.increment(vswitch_idl.OVSREC_OPEN_VSWITCH_COL_NEXT_CFG) 1109 1110 # TODO:XXX 1111 # symtab = ovsdb_symbol_table_create() 1112 ctx = VSCtlContext(idl_, self.txn, ovs_) 1113 for command in commands: 1114 if not command._run: 1115 continue 1116 command._run(ctx, command) 1117 if ctx.try_again: 1118 return False 1119 LOG.debug('result:\n%s', [command.result for command in commands]) 1120 ctx.done() 1121 1122 # TODO:XXX check if created symbols are really created, referenced. 1123 1124 status = self.txn.commit_block() 1125 next_cfg = 0 1126 if self.wait_for_reload and status == idl.Transaction.SUCCESS: 1127 next_cfg = self.txn.get_increment_new_value() 1128 1129 # TODO:XXX 1130 # if status in (idl.Transaction.UNCHANGED, idl.Transaction.SUCCESS): 1131 # for command in commands: 1132 # if not command.post_func: 1133 # continue 1134 # ctx = VSCtlContext(idl_, txn, self.ovs) 1135 # command.post_func(ctx) 1136 # ctx.done() 1137 1138 txn_ = self.txn 1139 self.txn = None 1140 1141 if status in (idl.Transaction.UNCOMMITTED, idl.Transaction.INCOMPLETE): 1142 not_reached() 1143 elif status == idl.Transaction.ABORTED: 1144 vsctl_fatal('transaction aborted') 1145 elif status == idl.Transaction.UNCHANGED: 1146 LOG.debug('unchanged') 1147 elif status == idl.Transaction.SUCCESS: 1148 LOG.debug('success') 1149 elif status == idl.Transaction.TRY_AGAIN: 1150 return False 1151 elif status == idl.Transaction.ERROR: 1152 vsctl_fatal('transaction error: %s' % txn_.get_error()) 1153 elif status == idl.Transaction.NOT_LOCKED: 1154 vsctl_fatal('database not locked') 1155 else: 1156 not_reached() 1157 1158 if self.wait_for_reload and status != idl.Transaction.UNCHANGED: 1159 while True: 1160 idl_.run() 1161 if ovs_.cur_cfg >= next_cfg: 1162 break 1163 self._idl_block(idl_) 1164 1165 return True 1166 1167 def _do_main(self, commands): 1168 """ 1169 :type commands: list of VSCtlCommand 1170 """ 1171 self._reset() 1172 self._init_schema_helper() 1173 self._run_prerequisites(commands) 1174 1175 idl_ = idl.Idl(self.remote, self.schema_helper) 1176 seqno = idl_.change_seqno 1177 while True: 1178 self._idl_wait(idl_, seqno) 1179 1180 seqno = idl_.change_seqno 1181 if self._do_vsctl(idl_, commands): 1182 break 1183 1184 if self.txn: 1185 self.txn.abort() 1186 self.txn = None 1187 # TODO:XXX 1188 # ovsdb_symbol_table_destroy(symtab) 1189 1190 idl_.close() 1191 1192 def _run_command(self, commands): 1193 """ 1194 :type commands: list of VSCtlCommand 1195 """ 1196 all_commands = { 1197 # Open vSwitch commands. 1198 'init': (None, self._cmd_init), 1199 'show': (self._pre_cmd_show, self._cmd_show), 1200 # 'emer-reset': 1201 1202 # Bridge commands. 1203 'add-br': (self._pre_add_br, self._cmd_add_br), 1204 'del-br': (self._pre_get_info, self._cmd_del_br), 1205 'list-br': (self._pre_get_info, self._cmd_list_br), 1206 'br-exists': (self._pre_get_info, self._cmd_br_exists), 1207 'br-to-vlan': (self._pre_get_info, self._cmd_br_to_vlan), 1208 'br-to-parent': (self._pre_get_info, self._cmd_br_to_parent), 1209 'br-set-external-id': (self._pre_cmd_br_set_external_id, 1210 self._cmd_br_set_external_id), 1211 'br-get-external-id': (self._pre_cmd_br_get_external_id, 1212 self._cmd_br_get_external_id), 1213 1214 # Port. commands 1215 'list-ports': (self._pre_get_info, self._cmd_list_ports), 1216 'add-port': (self._pre_cmd_add_port, self._cmd_add_port), 1217 'add-bond': (self._pre_cmd_add_bond, self._cmd_add_bond), 1218 'del-port': (self._pre_get_info, self._cmd_del_port), 1219 'port-to-br': (self._pre_get_info, self._cmd_port_to_br), 1220 1221 # Interface commands. 1222 'list-ifaces': (self._pre_get_info, self._cmd_list_ifaces), 1223 'iface-to-br': (self._pre_get_info, self._cmd_iface_to_br), 1224 1225 # Controller commands. 1226 'get-controller': (self._pre_controller, self._cmd_get_controller), 1227 'del-controller': (self._pre_controller, self._cmd_del_controller), 1228 'set-controller': (self._pre_controller, self._cmd_set_controller), 1229 'get-fail-mode': (self._pre_fail_mode, self._cmd_get_fail_mode), 1230 'del-fail-mode': (self._pre_fail_mode, self._cmd_del_fail_mode), 1231 'set-fail-mode': (self._pre_fail_mode, self._cmd_set_fail_mode), 1232 1233 # Manager commands. 1234 # 'get-manager': 1235 # 'del-manager': 1236 # 'set-manager': 1237 1238 # SSL commands. 1239 # 'get-ssl': 1240 # 'del-ssl': 1241 # 'set-ssl': 1242 1243 # Auto Attach commands. 1244 # 'add-aa-mapping': 1245 # 'del-aa-mapping': 1246 # 'get-aa-mapping': 1247 1248 # Switch commands. 1249 # 'emer-reset': 1250 1251 # Database commands. 1252 'list': (self._pre_cmd_list, self._cmd_list), 1253 'find': (self._pre_cmd_find, self._cmd_find), 1254 'get': (self._pre_cmd_get, self._cmd_get), 1255 'set': (self._pre_cmd_set, self._cmd_set), 1256 'add': (self._pre_cmd_add, self._cmd_add), 1257 'remove': (self._pre_cmd_remove, self._cmd_remove), 1258 'clear': (self._pre_cmd_clear, self._cmd_clear), 1259 # 'create': 1260 # 'destroy': 1261 # 'wait-until': 1262 1263 # Utility commands. (No corresponding command in ovs-vsctl) 1264 'set-qos': (self._pre_cmd_set_qos, self._cmd_set_qos), 1265 'set-queue': (self._pre_cmd_set_queue, self._cmd_set_queue), 1266 'del-qos': (self._pre_get_info, self._cmd_del_qos), 1267 # for quantum_adapter 1268 'list-ifaces-verbose': (self._pre_cmd_list_ifaces_verbose, 1269 self._cmd_list_ifaces_verbose), 1270 } 1271 1272 for command in commands: 1273 funcs = all_commands[command.command] 1274 command._prerequisite, command._run = funcs 1275 self._do_main(commands) 1276 1277 def run_command(self, commands, timeout_sec=None, exception=None): 1278 """ 1279 Executes the given commands and sends OVSDB messages. 1280 1281 ``commands`` must be a list of 1282 :py:mod:`ryu.lib.ovs.vsctl.VSCtlCommand`. 1283 1284 If ``timeout_sec`` is specified, raises exception after the given 1285 timeout [sec]. Additionally, if ``exception`` is specified, this 1286 function will wraps exception using the given exception class. 1287 1288 Retruns ``None`` but fills ``result`` attribute for each command 1289 instance. 1290 """ 1291 if timeout_sec is None: 1292 self._run_command(commands) 1293 else: 1294 with hub.Timeout(timeout_sec, exception): 1295 self._run_command(commands) 1296 1297 # Open vSwitch commands: 1298 1299 def _cmd_init(self, _ctx, _command): 1300 # nothing. Just check connection to ovsdb 1301 pass 1302 1303 _CMD_SHOW_TABLES = [ 1304 _CmdShowTable(vswitch_idl.OVSREC_TABLE_OPEN_VSWITCH, None, 1305 [vswitch_idl.OVSREC_OPEN_VSWITCH_COL_MANAGER_OPTIONS, 1306 vswitch_idl.OVSREC_OPEN_VSWITCH_COL_BRIDGES, 1307 vswitch_idl.OVSREC_OPEN_VSWITCH_COL_OVS_VERSION], 1308 False), 1309 _CmdShowTable(vswitch_idl.OVSREC_TABLE_BRIDGE, 1310 vswitch_idl.OVSREC_BRIDGE_COL_NAME, 1311 [vswitch_idl.OVSREC_BRIDGE_COL_CONTROLLER, 1312 vswitch_idl.OVSREC_BRIDGE_COL_FAIL_MODE, 1313 vswitch_idl.OVSREC_BRIDGE_COL_PORTS], 1314 False), 1315 _CmdShowTable(vswitch_idl.OVSREC_TABLE_PORT, 1316 vswitch_idl.OVSREC_PORT_COL_NAME, 1317 [vswitch_idl.OVSREC_PORT_COL_TAG, 1318 vswitch_idl.OVSREC_PORT_COL_TRUNKS, 1319 vswitch_idl.OVSREC_PORT_COL_INTERFACES], 1320 False), 1321 _CmdShowTable(vswitch_idl.OVSREC_TABLE_INTERFACE, 1322 vswitch_idl.OVSREC_INTERFACE_COL_NAME, 1323 [vswitch_idl.OVSREC_INTERFACE_COL_TYPE, 1324 vswitch_idl.OVSREC_INTERFACE_COL_OPTIONS], 1325 False), 1326 _CmdShowTable(vswitch_idl.OVSREC_TABLE_CONTROLLER, 1327 vswitch_idl.OVSREC_CONTROLLER_COL_TARGET, 1328 [vswitch_idl.OVSREC_CONTROLLER_COL_IS_CONNECTED], 1329 False), 1330 _CmdShowTable(vswitch_idl.OVSREC_TABLE_MANAGER, 1331 vswitch_idl.OVSREC_MANAGER_COL_TARGET, 1332 [vswitch_idl.OVSREC_MANAGER_COL_IS_CONNECTED], 1333 False), 1334 ] 1335 1336 def _pre_cmd_show(self, _ctx, _command): 1337 schema_helper = self.schema_helper 1338 for show in self._CMD_SHOW_TABLES: 1339 schema_helper.register_table(show.table) 1340 if show.name_column: 1341 schema_helper.register_columns(show.table, [show.name_column]) 1342 schema_helper.register_columns(show.table, show.columns) 1343 1344 @staticmethod 1345 def _cmd_show_find_table_by_row(row): 1346 for show in VSCtl._CMD_SHOW_TABLES: 1347 if show.table == row._table.name: 1348 return show 1349 return None 1350 1351 @staticmethod 1352 def _cmd_show_find_table_by_name(name): 1353 for show in VSCtl._CMD_SHOW_TABLES: 1354 if show.table == name: 1355 return show 1356 return None 1357 1358 @staticmethod 1359 def _cmd_show_row(ctx, row, level): 1360 _INDENT_SIZE = 4 # # of spaces per indent 1361 show = VSCtl._cmd_show_find_table_by_row(row) 1362 output = '' 1363 1364 output += ' ' * level * _INDENT_SIZE 1365 if show and show.name_column: 1366 output += '%s ' % show.table 1367 datum = getattr(row, show.name_column) 1368 output += datum 1369 else: 1370 output += str(row.uuid) 1371 output += '\n' 1372 1373 if not show or show.recurse: 1374 return 1375 1376 show.recurse = True 1377 for column in show.columns: 1378 datum = row._data[column] 1379 key = datum.type.key 1380 if key.type == ovs.db.types.UuidType and key.ref_table_name: 1381 ref_show = VSCtl._cmd_show_find_table_by_name( 1382 key.ref_table_name) 1383 if ref_show: 1384 for atom in datum.values: 1385 ref_row = ctx.idl.tables[ref_show.table].rows.get( 1386 atom.value) 1387 if ref_row: 1388 VSCtl._cmd_show_row(ctx, ref_row, level + 1) 1389 continue 1390 1391 if not datum.is_default(): 1392 output += ' ' * (level + 1) * _INDENT_SIZE 1393 output += '%s: %s\n' % (column, datum) 1394 1395 show.recurse = False 1396 return output 1397 1398 def _cmd_show(self, ctx, command): 1399 for row in ctx.idl.tables[ 1400 self._CMD_SHOW_TABLES[0].table].rows.values(): 1401 output = self._cmd_show_row(ctx, row, 0) 1402 command.result = output 1403 1404 # Bridge commands: 1405 1406 def _pre_get_info(self, _ctx, _command): 1407 schema_helper = self.schema_helper 1408 1409 schema_helper.register_columns( 1410 vswitch_idl.OVSREC_TABLE_OPEN_VSWITCH, 1411 [vswitch_idl.OVSREC_OPEN_VSWITCH_COL_BRIDGES]) 1412 schema_helper.register_columns( 1413 vswitch_idl.OVSREC_TABLE_BRIDGE, 1414 [vswitch_idl.OVSREC_BRIDGE_COL_NAME, 1415 vswitch_idl.OVSREC_BRIDGE_COL_CONTROLLER, 1416 vswitch_idl.OVSREC_BRIDGE_COL_FAIL_MODE, 1417 vswitch_idl.OVSREC_BRIDGE_COL_PORTS]) 1418 schema_helper.register_columns( 1419 vswitch_idl.OVSREC_TABLE_PORT, 1420 [vswitch_idl.OVSREC_PORT_COL_NAME, 1421 vswitch_idl.OVSREC_PORT_COL_FAKE_BRIDGE, 1422 vswitch_idl.OVSREC_PORT_COL_TAG, 1423 vswitch_idl.OVSREC_PORT_COL_INTERFACES, 1424 vswitch_idl.OVSREC_PORT_COL_QOS]) 1425 schema_helper.register_columns( 1426 vswitch_idl.OVSREC_TABLE_INTERFACE, 1427 [vswitch_idl.OVSREC_INTERFACE_COL_NAME]) 1428 schema_helper.register_columns( 1429 vswitch_idl.OVSREC_TABLE_QOS, 1430 [vswitch_idl.OVSREC_QOS_COL_QUEUES]) 1431 schema_helper.register_columns( 1432 vswitch_idl.OVSREC_TABLE_QUEUE, 1433 []) 1434 1435 def _cmd_list_br(self, ctx, command): 1436 ctx.populate_cache() 1437 command.result = sorted(ctx.bridges.keys()) 1438 1439 def _pre_add_br(self, ctx, command): 1440 self._pre_get_info(ctx, command) 1441 1442 schema_helper = self.schema_helper 1443 schema_helper.register_columns( 1444 vswitch_idl.OVSREC_TABLE_INTERFACE, 1445 [vswitch_idl.OVSREC_INTERFACE_COL_TYPE]) 1446 1447 def _cmd_add_br(self, ctx, command): 1448 br_name = command.args[0] 1449 parent_name = None 1450 vlan = 0 1451 if len(command.args) == 1: 1452 pass 1453 elif len(command.args) == 3: 1454 parent_name = command.args[1] 1455 vlan = int(command.args[2]) 1456 if vlan < 0 or vlan > 4095: 1457 vsctl_fatal("vlan must be between 0 and 4095 %d" % vlan) 1458 else: 1459 vsctl_fatal('this command takes exactly 1 or 3 argument') 1460 1461 ctx.add_bridge(br_name, parent_name, vlan) 1462 1463 def _del_br(self, ctx, br_name, must_exist=False): 1464 ctx.populate_cache() 1465 br = ctx.find_bridge(br_name, must_exist) 1466 if br: 1467 ctx.del_bridge(br) 1468 1469 def _cmd_del_br(self, ctx, command): 1470 br_name = command.args[0] 1471 self._del_br(ctx, br_name) 1472 1473 def _br_exists(self, ctx, br_name): 1474 ctx.populate_cache() 1475 br = ctx.find_bridge(br_name, must_exist=False) 1476 return br is not None 1477 1478 def _cmd_br_exists(self, ctx, command): 1479 br_name = command.args[0] 1480 command.result = self._br_exists(ctx, br_name) 1481 1482 def _br_to_vlan(self, ctx, br_name): 1483 ctx.populate_cache() 1484 br = ctx.find_bridge(br_name, must_exist=True) 1485 vlan = br.vlan 1486 if isinstance(vlan, list): 1487 if len(vlan) == 0: 1488 vlan = 0 1489 else: 1490 vlan = vlan[0] 1491 return vlan 1492 1493 def _cmd_br_to_vlan(self, ctx, command): 1494 br_name = command.args[0] 1495 command.result = self._br_to_vlan(ctx, br_name) 1496 1497 def _br_to_parent(self, ctx, br_name): 1498 ctx.populate_cache() 1499 br = ctx.find_bridge(br_name, must_exist=True) 1500 return br if br.parent is None else br.parent 1501 1502 def _cmd_br_to_parent(self, ctx, command): 1503 br_name = command.args[0] 1504 command.result = self._br_to_parent(ctx, br_name) 1505 1506 def _pre_cmd_br_set_external_id(self, ctx, _command): 1507 table_name = vswitch_idl.OVSREC_TABLE_BRIDGE 1508 columns = [vswitch_idl.OVSREC_BRIDGE_COL_EXTERNAL_IDS] 1509 self._pre_mod_columns(ctx, table_name, columns) 1510 1511 def _br_add_external_id(self, ctx, br_name, key, value): 1512 table_name = vswitch_idl.OVSREC_TABLE_BRIDGE 1513 column = vswitch_idl.OVSREC_BRIDGE_COL_EXTERNAL_IDS 1514 vsctl_table = self._get_table(table_name) 1515 ovsrec_row = ctx.must_get_row(vsctl_table, br_name) 1516 1517 value_json = ['map', [[key, value]]] 1518 ctx.add_column(ovsrec_row, column, value_json) 1519 ctx.invalidate_cache() 1520 1521 def _br_clear_external_id(self, ctx, br_name, key): 1522 table_name = vswitch_idl.OVSREC_TABLE_BRIDGE 1523 column = vswitch_idl.OVSREC_BRIDGE_COL_EXTERNAL_IDS 1524 vsctl_table = self._get_table(table_name) 1525 ovsrec_row = ctx.must_get_row(vsctl_table, br_name) 1526 1527 values = getattr(ovsrec_row, column, {}) 1528 values.pop(key, None) 1529 setattr(ovsrec_row, column, values) 1530 ctx.invalidate_cache() 1531 1532 def _cmd_br_set_external_id(self, ctx, command): 1533 br_name = command.args[0] 1534 key = command.args[1] 1535 if len(command.args) > 2: 1536 self._br_add_external_id(ctx, br_name, key, command.args[2]) 1537 else: 1538 self._br_clear_external_id(ctx, br_name, key) 1539 1540 def _pre_cmd_br_get_external_id(self, ctx, _command): 1541 table_name = vswitch_idl.OVSREC_TABLE_BRIDGE 1542 columns = [vswitch_idl.OVSREC_BRIDGE_COL_EXTERNAL_IDS] 1543 self._pre_get_columns(ctx, table_name, columns) 1544 1545 def _br_get_external_id_value(self, ctx, br_name, key): 1546 external_id = self._br_get_external_id_list(ctx, br_name) 1547 1548 return external_id.get(key, None) 1549 1550 def _br_get_external_id_list(self, ctx, br_name): 1551 table_name = vswitch_idl.OVSREC_TABLE_BRIDGE 1552 column = vswitch_idl.OVSREC_BRIDGE_COL_EXTERNAL_IDS 1553 vsctl_table = self._get_table(table_name) 1554 ovsrec_row = ctx.must_get_row(vsctl_table, br_name) 1555 1556 return ctx.get_column(ovsrec_row, column) 1557 1558 def _cmd_br_get_external_id(self, ctx, command): 1559 br_name = command.args[0] 1560 if len(command.args) > 1: 1561 command.result = self._br_get_external_id_value(ctx, br_name, 1562 command.args[1]) 1563 else: 1564 command.result = self._br_get_external_id_list(ctx, br_name) 1565 1566 # Port commands: 1567 1568 def _list_ports(self, ctx, br_name): 1569 ctx.populate_cache() 1570 br = ctx.find_bridge(br_name, True) 1571 if br.br_cfg: 1572 br.br_cfg.verify(vswitch_idl.OVSREC_BRIDGE_COL_PORTS) 1573 else: 1574 br.parent.br_cfg.verify(vswitch_idl.OVSREC_BRIDGE_COL_PORTS) 1575 1576 return [port.port_cfg.name for port in br.ports 1577 if port.port_cfg.name != br.name] 1578 1579 def _cmd_list_ports(self, ctx, command): 1580 br_name = command.args[0] 1581 port_names = self._list_ports(ctx, br_name) 1582 command.result = sorted(port_names) 1583 1584 def _pre_add_port(self, _ctx, columns): 1585 schema_helper = self.schema_helper 1586 schema_helper.register_columns( 1587 vswitch_idl.OVSREC_TABLE_PORT, 1588 [vswitch_idl.OVSREC_PORT_COL_NAME, 1589 vswitch_idl.OVSREC_PORT_COL_BOND_FAKE_IFACE]) 1590 schema_helper.register_columns( 1591 vswitch_idl.OVSREC_TABLE_PORT, columns) 1592 1593 def _pre_cmd_add_port(self, ctx, command): 1594 self._pre_get_info(ctx, command) 1595 1596 columns = [ 1597 ctx.parse_column_key_value( 1598 self.schema.tables[vswitch_idl.OVSREC_TABLE_PORT], setting)[0] 1599 for setting in command.args[2:]] 1600 1601 self._pre_add_port(ctx, columns) 1602 1603 def _pre_cmd_add_bond(self, ctx, command): 1604 self._pre_get_info(ctx, command) 1605 1606 if len(command.args) < 3: 1607 vsctl_fatal('this command requires at least 3 arguments') 1608 1609 columns = [ 1610 ctx.parse_column_key_value( 1611 self.schema.tables[vswitch_idl.OVSREC_TABLE_PORT], setting)[0] 1612 for setting in command.args[3:]] 1613 1614 self._pre_add_port(ctx, columns) 1615 1616 def _cmd_add_port(self, ctx, command): 1617 # '--may_exist' is a typo but for backword compatibility 1618 may_exist = (command.has_option('--may_exist') 1619 or command.has_option('--may-exist')) 1620 1621 br_name = command.args[0] 1622 port_name = command.args[1] 1623 iface_names = [command.args[1]] 1624 settings = [ 1625 ctx.parse_column_key_value( 1626 self.schema.tables[vswitch_idl.OVSREC_TABLE_PORT], setting) 1627 for setting in command.args[2:]] 1628 1629 ctx.add_port(br_name, port_name, may_exist, 1630 False, iface_names, settings) 1631 1632 def _cmd_add_bond(self, ctx, command): 1633 # '--may_exist' is a typo but for backword compatibility 1634 may_exist = (command.has_option('--may_exist') 1635 or command.has_option('--may-exist')) 1636 fake_iface = command.has_option('--fake-iface') 1637 1638 br_name = command.args[0] 1639 port_name = command.args[1] 1640 iface_names = list(command.args[2]) 1641 settings = [ 1642 ctx.parse_column_key_value( 1643 self.schema.tables[vswitch_idl.OVSREC_TABLE_PORT], setting) 1644 for setting in command.args[3:]] 1645 1646 ctx.add_port(br_name, port_name, may_exist, fake_iface, 1647 iface_names, settings) 1648 1649 def _del_port(self, ctx, br_name=None, target=None, 1650 must_exist=False, with_iface=False): 1651 assert target is not None 1652 1653 ctx.populate_cache() 1654 if not with_iface: 1655 vsctl_port = ctx.find_port(target, must_exist) 1656 else: 1657 vsctl_port = ctx.find_port(target, False) 1658 if not vsctl_port: 1659 vsctl_iface = ctx.find_iface(target, False) 1660 if vsctl_iface: 1661 vsctl_port = vsctl_iface.port() 1662 if must_exist and not vsctl_port: 1663 vsctl_fatal('no port or interface named %s' % target) 1664 1665 if not vsctl_port: 1666 return 1667 if not br_name: 1668 vsctl_bridge = ctx.find_bridge(br_name, True) 1669 if vsctl_port.bridge() != vsctl_bridge: 1670 if vsctl_port.bridge().parent == vsctl_bridge: 1671 vsctl_fatal('bridge %s does not have a port %s (although ' 1672 'its parent bridge %s does)' % 1673 (br_name, target, vsctl_bridge.parent.name)) 1674 else: 1675 vsctl_fatal('bridge %s does not have a port %s' % 1676 (br_name, target)) 1677 1678 ctx.del_port(vsctl_port) 1679 1680 def _cmd_del_port(self, ctx, command): 1681 must_exist = command.has_option('--must-exist') 1682 with_iface = command.has_option('--with-iface') 1683 target = command.args[-1] 1684 br_name = command.args[0] if len(command.args) == 2 else None 1685 self._del_port(ctx, br_name, target, must_exist, with_iface) 1686 1687 def _port_to_br(self, ctx, port_name): 1688 ctx.populate_cache() 1689 port = ctx.find_port(port_name, True) 1690 bridge = port.bridge() 1691 if bridge is None: 1692 vsctl_fatal('Bridge associated to port "%s" does not exist' % 1693 port_name) 1694 1695 return bridge.name 1696 1697 def _cmd_port_to_br(self, ctx, command): 1698 iface_name = command.args[0] 1699 command.result = self._iface_to_br(ctx, iface_name) 1700 1701 # Interface commands: 1702 1703 def _list_ifaces(self, ctx, br_name): 1704 ctx.populate_cache() 1705 1706 br = ctx.find_bridge(br_name, True) 1707 ctx.verify_ports() 1708 1709 iface_names = set() 1710 for vsctl_port in br.ports: 1711 for vsctl_iface in vsctl_port.ifaces: 1712 iface_name = vsctl_iface.iface_cfg.name 1713 if iface_name != br_name: 1714 iface_names.add(iface_name) 1715 return iface_names 1716 1717 def _cmd_list_ifaces(self, ctx, command): 1718 br_name = command.args[0] 1719 iface_names = self._list_ifaces(ctx, br_name) 1720 command.result = sorted(iface_names) 1721 1722 def _iface_to_br(self, ctx, iface_name): 1723 ctx.populate_cache() 1724 iface = ctx.find_iface(iface_name, True) 1725 port = iface.port() 1726 if port is None: 1727 vsctl_fatal('Port associated to iface "%s" does not exist' % 1728 iface_name) 1729 bridge = port.bridge() 1730 if bridge is None: 1731 vsctl_fatal('Bridge associated to iface "%s" does not exist' % 1732 iface_name) 1733 1734 return bridge.name 1735 1736 def _cmd_iface_to_br(self, ctx, command): 1737 iface_name = command.args[0] 1738 command.result = self._iface_to_br(ctx, iface_name) 1739 1740 # Utility commands for quantum_adapter: 1741 1742 def _pre_cmd_list_ifaces_verbose(self, ctx, command): 1743 self._pre_get_info(ctx, command) 1744 schema_helper = self.schema_helper 1745 schema_helper.register_columns( 1746 vswitch_idl.OVSREC_TABLE_BRIDGE, 1747 [vswitch_idl.OVSREC_BRIDGE_COL_DATAPATH_ID]) 1748 schema_helper.register_columns( 1749 vswitch_idl.OVSREC_TABLE_INTERFACE, 1750 [vswitch_idl.OVSREC_INTERFACE_COL_TYPE, 1751 vswitch_idl.OVSREC_INTERFACE_COL_NAME, 1752 vswitch_idl.OVSREC_INTERFACE_COL_EXTERNAL_IDS, 1753 vswitch_idl.OVSREC_INTERFACE_COL_OPTIONS, 1754 vswitch_idl.OVSREC_INTERFACE_COL_OFPORT]) 1755 1756 @staticmethod 1757 def _iface_to_dict(iface_cfg): 1758 _ATTRIBUTE = ['name', 'ofport', 'type', 'external_ids', 'options'] 1759 attr = dict((key, getattr(iface_cfg, key)) for key in _ATTRIBUTE) 1760 1761 if attr['ofport']: 1762 attr['ofport'] = attr['ofport'][0] 1763 return attr 1764 1765 def _list_ifaces_verbose(self, ctx, datapath_id, port_name): 1766 ctx.populate_cache() 1767 1768 br = ctx.find_bridge_by_id(datapath_id, True) 1769 ctx.verify_ports() 1770 1771 iface_cfgs = [] 1772 if port_name is None: 1773 for vsctl_port in br.ports: 1774 iface_cfgs.extend(self._iface_to_dict(vsctl_iface.iface_cfg) 1775 for vsctl_iface in vsctl_port.ifaces) 1776 else: 1777 # When port is created, ofport column might be None. 1778 # So try with port name if it happended 1779 for vsctl_port in br.ports: 1780 iface_cfgs.extend( 1781 self._iface_to_dict(vsctl_iface.iface_cfg) 1782 for vsctl_iface in vsctl_port.ifaces 1783 if vsctl_iface.iface_cfg.name == port_name) 1784 1785 return iface_cfgs 1786 1787 def _cmd_list_ifaces_verbose(self, ctx, command): 1788 datapath_id = command.args[0] 1789 port_name = None 1790 if len(command.args) >= 2: 1791 port_name = command.args[1] 1792 LOG.debug('command.args %s', command.args) 1793 iface_cfgs = self._list_ifaces_verbose(ctx, datapath_id, port_name) 1794 command.result = sorted(iface_cfgs) 1795 1796 # Controller commands: 1797 1798 def _verify_controllers(self, ovsrec_bridge): 1799 ovsrec_bridge.verify(vswitch_idl.OVSREC_BRIDGE_COL_CONTROLLER) 1800 for controller in ovsrec_bridge.controller: 1801 controller.verify(vswitch_idl.OVSREC_CONTROLLER_COL_TARGET) 1802 1803 def _pre_controller(self, ctx, command): 1804 self._pre_get_info(ctx, command) 1805 self.schema_helper.register_columns( 1806 vswitch_idl.OVSREC_TABLE_CONTROLLER, 1807 [vswitch_idl.OVSREC_CONTROLLER_COL_TARGET]) 1808 1809 def _get_controller(self, ctx, br_name): 1810 ctx.populate_cache() 1811 br = ctx.find_bridge(br_name, True) 1812 self._verify_controllers(br.br_cfg) 1813 return set(controller.target for controller in br.br_cfg.controller) 1814 1815 def _cmd_get_controller(self, ctx, command): 1816 br_name = command.args[0] 1817 controller_names = self._get_controller(ctx, br_name) 1818 command.result = sorted(controller_names) 1819 1820 def _delete_controllers(self, ovsrec_controllers): 1821 for controller in ovsrec_controllers: 1822 controller.delete() 1823 1824 def _del_controller(self, ctx, br_name): 1825 ctx.populate_cache() 1826 br = ctx.find_real_bridge(br_name, True) 1827 ovsrec_bridge = br.br_cfg 1828 self._verify_controllers(ovsrec_bridge) 1829 if ovsrec_bridge.controller: 1830 self._delete_controllers(ovsrec_bridge.controller) 1831 ovsrec_bridge.controller = [] 1832 1833 def _cmd_del_controller(self, ctx, command): 1834 br_name = command.args[0] 1835 self._del_controller(ctx, br_name) 1836 1837 def _insert_controllers(self, controller_names): 1838 ovsrec_controllers = [] 1839 for name in controller_names: 1840 # TODO: check if the name startswith() supported protocols 1841 ovsrec_controller = self.txn.insert( 1842 self.txn.idl.tables[vswitch_idl.OVSREC_TABLE_CONTROLLER]) 1843 ovsrec_controller.target = name 1844 ovsrec_controllers.append(ovsrec_controller) 1845 return ovsrec_controllers 1846 1847 def _insert_qos(self): 1848 ovsrec_qos = self.txn.insert( 1849 self.txn.idl.tables[vswitch_idl.OVSREC_TABLE_QOS]) 1850 1851 return ovsrec_qos 1852 1853 def _set_controller(self, ctx, br_name, controller_names): 1854 ctx.populate_cache() 1855 ovsrec_bridge = ctx.find_real_bridge(br_name, True).br_cfg 1856 self._verify_controllers(ovsrec_bridge) 1857 self._delete_controllers(ovsrec_bridge.controller) 1858 controllers = self._insert_controllers(controller_names) 1859 ovsrec_bridge.controller = controllers 1860 1861 def _cmd_set_controller(self, ctx, command): 1862 br_name = command.args[0] 1863 controller_names = command.args[1:] 1864 self._set_controller(ctx, br_name, controller_names) 1865 1866 def _pre_fail_mode(self, ctx, command): 1867 self._pre_get_info(ctx, command) 1868 self.schema_helper.register_columns( 1869 vswitch_idl.OVSREC_TABLE_BRIDGE, 1870 [vswitch_idl.OVSREC_BRIDGE_COL_FAIL_MODE]) 1871 1872 def _get_fail_mode(self, ctx, br_name): 1873 ctx.populate_cache() 1874 br = ctx.find_bridge(br_name, True) 1875 1876 # Note: Returns first element of fail_mode column 1877 return getattr(br.br_cfg, vswitch_idl.OVSREC_BRIDGE_COL_FAIL_MODE)[0] 1878 1879 def _cmd_get_fail_mode(self, ctx, command): 1880 br_name = command.args[0] 1881 command.result = self._get_fail_mode(ctx, br_name) 1882 1883 def _del_fail_mode(self, ctx, br_name): 1884 ctx.populate_cache() 1885 br = ctx.find_bridge(br_name, True) 1886 # Note: assuming that [] means empty 1887 setattr(br.br_cfg, vswitch_idl.OVSREC_BRIDGE_COL_FAIL_MODE, []) 1888 ctx.invalidate_cache() 1889 1890 def _cmd_del_fail_mode(self, ctx, command): 1891 br_name = command.args[0] 1892 self._del_fail_mode(ctx, br_name) 1893 1894 def _set_fail_mode(self, ctx, br_name, mode): 1895 ctx.populate_cache() 1896 br = ctx.find_bridge(br_name, True) 1897 setattr(br.br_cfg, vswitch_idl.OVSREC_BRIDGE_COL_FAIL_MODE, mode) 1898 ctx.invalidate_cache() 1899 1900 def _cmd_set_fail_mode(self, ctx, command): 1901 br_name = command.args[0] 1902 mode = command.args[1] 1903 if mode not in ('standalone', 'secure'): 1904 vsctl_fatal('fail-mode must be "standalone" or "secure"') 1905 self._set_fail_mode(ctx, br_name, mode) 1906 1907 # Utility commands: 1908 1909 def _del_qos(self, ctx, port_name): 1910 assert port_name is not None 1911 1912 ctx.populate_cache() 1913 vsctl_port = ctx.find_port(port_name, True) 1914 vsctl_qos = vsctl_port.qos 1915 ctx.del_qos(vsctl_qos) 1916 1917 def _cmd_del_qos(self, ctx, command): 1918 port_name = command.args[0] 1919 self._del_qos(ctx, port_name) 1920 1921 def _set_qos(self, ctx, port_name, type, max_rate): 1922 ctx.populate_cache() 1923 vsctl_port = ctx.find_port(port_name, True) 1924 ovsrec_qos = ctx.set_qos(vsctl_port, type, max_rate) 1925 return ovsrec_qos 1926 1927 def _cmd_set_qos(self, ctx, command): 1928 port_name = command.args[0] 1929 type = command.args[1] 1930 max_rate = command.args[2] 1931 result = self._set_qos(ctx, port_name, type, max_rate) 1932 command.result = [result] 1933 1934 def _pre_cmd_set_qos(self, ctx, command): 1935 self._pre_get_info(ctx, command) 1936 schema_helper = self.schema_helper 1937 schema_helper.register_columns( 1938 vswitch_idl.OVSREC_TABLE_QOS, 1939 [vswitch_idl.OVSREC_QOS_COL_EXTERNAL_IDS, 1940 vswitch_idl.OVSREC_QOS_COL_OTHER_CONFIG, 1941 vswitch_idl.OVSREC_QOS_COL_QUEUES, 1942 vswitch_idl.OVSREC_QOS_COL_TYPE]) 1943 1944 def _cmd_set_queue(self, ctx, command): 1945 ctx.populate_cache() 1946 port_name = command.args[0] 1947 queues = command.args[1] 1948 vsctl_port = ctx.find_port(port_name, True) 1949 vsctl_qos = vsctl_port.qos 1950 queue_id = 0 1951 results = [] 1952 for queue in queues: 1953 max_rate = queue.get('max-rate', None) 1954 min_rate = queue.get('min-rate', None) 1955 ovsrec_queue = ctx.set_queue( 1956 vsctl_qos, max_rate, min_rate, queue_id) 1957 results.append(ovsrec_queue) 1958 queue_id += 1 1959 command.result = results 1960 1961 def _pre_cmd_set_queue(self, ctx, command): 1962 self._pre_get_info(ctx, command) 1963 schema_helper = self.schema_helper 1964 schema_helper.register_columns( 1965 vswitch_idl.OVSREC_TABLE_QUEUE, 1966 [vswitch_idl.OVSREC_QUEUE_COL_DSCP, 1967 vswitch_idl.OVSREC_QUEUE_COL_EXTERNAL_IDS, 1968 vswitch_idl.OVSREC_QUEUE_COL_OTHER_CONFIG]) 1969 1970 # Database commands: 1971 1972 _TABLES = [ 1973 _VSCtlTable(vswitch_idl.OVSREC_TABLE_BRIDGE, 1974 [_VSCtlRowID(vswitch_idl.OVSREC_TABLE_BRIDGE, 1975 vswitch_idl.OVSREC_BRIDGE_COL_NAME, 1976 None)]), 1977 _VSCtlTable(vswitch_idl.OVSREC_TABLE_CONTROLLER, 1978 [_VSCtlRowID(vswitch_idl.OVSREC_TABLE_BRIDGE, 1979 vswitch_idl.OVSREC_BRIDGE_COL_NAME, 1980 vswitch_idl.OVSREC_BRIDGE_COL_CONTROLLER)]), 1981 _VSCtlTable(vswitch_idl.OVSREC_TABLE_INTERFACE, 1982 [_VSCtlRowID(vswitch_idl.OVSREC_TABLE_INTERFACE, 1983 vswitch_idl.OVSREC_INTERFACE_COL_NAME, 1984 None)]), 1985 _VSCtlTable(vswitch_idl.OVSREC_TABLE_MIRROR, 1986 [_VSCtlRowID(vswitch_idl.OVSREC_TABLE_MIRROR, 1987 vswitch_idl.OVSREC_MIRROR_COL_NAME, 1988 None)]), 1989 _VSCtlTable(vswitch_idl.OVSREC_TABLE_MANAGER, 1990 [_VSCtlRowID(vswitch_idl.OVSREC_TABLE_MANAGER, 1991 vswitch_idl.OVSREC_MANAGER_COL_TARGET, 1992 None)]), 1993 _VSCtlTable(vswitch_idl.OVSREC_TABLE_NETFLOW, 1994 [_VSCtlRowID(vswitch_idl.OVSREC_TABLE_BRIDGE, 1995 vswitch_idl.OVSREC_BRIDGE_COL_NAME, 1996 vswitch_idl.OVSREC_BRIDGE_COL_NETFLOW)]), 1997 _VSCtlTable(vswitch_idl.OVSREC_TABLE_OPEN_VSWITCH, 1998 [_VSCtlRowID(vswitch_idl.OVSREC_TABLE_OPEN_VSWITCH, 1999 None, 2000 None)]), 2001 _VSCtlTable(vswitch_idl.OVSREC_TABLE_PORT, 2002 [_VSCtlRowID(vswitch_idl.OVSREC_TABLE_PORT, 2003 vswitch_idl.OVSREC_PORT_COL_NAME, 2004 None)]), 2005 _VSCtlTable(vswitch_idl.OVSREC_TABLE_QOS, 2006 [_VSCtlRowID(vswitch_idl.OVSREC_TABLE_PORT, 2007 vswitch_idl.OVSREC_PORT_COL_NAME, 2008 vswitch_idl.OVSREC_PORT_COL_QOS)]), 2009 _VSCtlTable(vswitch_idl.OVSREC_TABLE_QUEUE, 2010 [_VSCtlRowID(vswitch_idl.OVSREC_TABLE_QOS, 2011 None, 2012 vswitch_idl.OVSREC_QOS_COL_QUEUES)]), 2013 _VSCtlTable(vswitch_idl.OVSREC_TABLE_SSL, 2014 [_VSCtlRowID(vswitch_idl.OVSREC_TABLE_OPEN_VSWITCH, 2015 None, 2016 vswitch_idl.OVSREC_OPEN_VSWITCH_COL_SSL)]), 2017 _VSCtlTable(vswitch_idl.OVSREC_TABLE_SFLOW, 2018 [_VSCtlRowID(vswitch_idl.OVSREC_TABLE_BRIDGE, 2019 vswitch_idl.OVSREC_BRIDGE_COL_NAME, 2020 vswitch_idl.OVSREC_BRIDGE_COL_SFLOW)]), 2021 _VSCtlTable(vswitch_idl.OVSREC_TABLE_FLOW_TABLE, 2022 [_VSCtlRowID(vswitch_idl.OVSREC_TABLE_FLOW_TABLE, 2023 vswitch_idl.OVSREC_FLOW_TABLE_COL_NAME, 2024 None)]), 2025 ] 2026 2027 @staticmethod 2028 def _score_partial_match(name, s): 2029 _MAX_SCORE = 0xffffffff 2030 assert len(name) < _MAX_SCORE 2031 s = s[:_MAX_SCORE - 1] # in practice, this doesn't matter 2032 if name == s: 2033 return _MAX_SCORE 2034 2035 name = name.lower().replace('-', '_') 2036 s = s.lower().replace('-', '_') 2037 if s.startswith(name): 2038 return _MAX_SCORE - 1 2039 if name.startswith(s): 2040 return len(s) 2041 2042 return 0 2043 2044 @staticmethod 2045 def _get_table(table_name): 2046 best_match = None 2047 best_score = 0 2048 for table in VSCtl._TABLES: 2049 score = VSCtl._score_partial_match(table.table_name, table_name) 2050 if score > best_score: 2051 best_match = table 2052 best_score = score 2053 elif score == best_score: 2054 best_match = None 2055 2056 if best_match: 2057 return best_match 2058 elif best_score: 2059 vsctl_fatal('multiple table names match "%s"' % table_name) 2060 else: 2061 vsctl_fatal('unknown table "%s"' % table_name) 2062 2063 def _pre_get_table(self, _ctx, table_name): 2064 vsctl_table = self._get_table(table_name) 2065 2066 schema_helper = self.schema_helper 2067 schema_helper.register_table(vsctl_table.table_name) 2068 for row_id in vsctl_table.row_ids: 2069 if row_id.table: 2070 schema_helper.register_table(row_id.table) 2071 if row_id.name_column: 2072 schema_helper.register_columns(row_id.table, 2073 [row_id.name_column]) 2074 if row_id.uuid_column: 2075 schema_helper.register_columns(row_id.table, 2076 [row_id.uuid_column]) 2077 return vsctl_table 2078 2079 def _get_column(self, table_name, column_name): 2080 best_match = None 2081 best_score = 0 2082 2083 columns = self.schema.tables[table_name].columns.keys() 2084 for column in columns: 2085 score = VSCtl._score_partial_match(column, column_name) 2086 if score > best_score: 2087 best_match = column 2088 best_score = score 2089 elif score == best_score: 2090 best_match = None 2091 2092 if best_match: 2093 # ovs.db.schema_helper._keep_table_columns() requires that 2094 # column_name is type of str. Not unicode string 2095 return str(best_match) 2096 elif best_score: 2097 vsctl_fatal('%s contains more than one column whose name ' 2098 'matches "%s"' % (table_name, column_name)) 2099 else: 2100 vsctl_fatal('%s does not contain a column whose name matches ' 2101 '"%s"' % (table_name, column_name)) 2102 2103 def _pre_get_column(self, _ctx, table_name, column): 2104 column_name = self._get_column(table_name, column) 2105 self.schema_helper.register_columns(table_name, [column_name]) 2106 2107 def _pre_get_columns(self, ctx, table_name, columns): 2108 self._pre_get_table(ctx, table_name) 2109 for column in columns: 2110 self._pre_get_column(ctx, table_name, column) 2111 2112 def _pre_cmd_list(self, ctx, command): 2113 table_name = command.args[0] 2114 self._pre_get_table(ctx, table_name) 2115 2116 def _list(self, ctx, table_name, record_id=None): 2117 result = [] 2118 for ovsrec_row in ctx.idl.tables[table_name].rows.values(): 2119 if record_id is not None and ovsrec_row.name != record_id: 2120 continue 2121 result.append(ovsrec_row) 2122 2123 return result 2124 2125 def _cmd_list(self, ctx, command): 2126 table_name = command.args[0] 2127 record_id = None 2128 if len(command.args) > 1: 2129 record_id = command.args[1] 2130 2131 command.result = self._list(ctx, table_name, record_id) 2132 2133 def _pre_cmd_find(self, ctx, command): 2134 table_name = command.args[0] 2135 table_schema = self.schema.tables[table_name] 2136 columns = [ 2137 ctx.parse_column_key_value(table_schema, column_key_value)[0] 2138 for column_key_value in command.args[1:]] 2139 2140 self._pre_get_columns(ctx, table_name, columns) 2141 2142 def _check_value(self, ovsrec_row, column_value): 2143 """ 2144 :type column_value: tuple of column and value_json 2145 """ 2146 column, value_json = column_value 2147 column_schema = ovsrec_row._table.columns[column] 2148 value = ovs.db.data.Datum.from_json( 2149 column_schema.type, value_json).to_python(ovs.db.idl._uuid_to_row) 2150 datum = getattr(ovsrec_row, column) 2151 if column_schema.type.is_map(): 2152 for k, v in value.items(): 2153 if k in datum and datum[k] == v: 2154 return True 2155 elif datum == value: 2156 return True 2157 2158 return False 2159 2160 def _find(self, ctx, table_name, column_values): 2161 """ 2162 :type column_values: list of (column, value_json) 2163 """ 2164 result = [] 2165 for ovsrec_row in ctx.idl.tables[table_name].rows.values(): 2166 LOG.debug('ovsrec_row %s', ovsrec_row_to_string(ovsrec_row)) 2167 if all(self._check_value(ovsrec_row, column_value) 2168 for column_value in column_values): 2169 result.append(ovsrec_row) 2170 2171 return result 2172 2173 def _cmd_find(self, ctx, command): 2174 table_name = command.args[0] 2175 table_schema = self.schema.tables[table_name] 2176 column_values = [ 2177 ctx.parse_column_key_value(table_schema, column_key_value) 2178 for column_key_value in command.args[1:]] 2179 command.result = self._find(ctx, table_name, column_values) 2180 2181 def _pre_cmd_get(self, ctx, command): 2182 table_name = command.args[0] 2183 columns = [ 2184 ctx.parse_column_key(column_key)[0] 2185 for column_key in command.args[2:]] 2186 2187 self._pre_get_columns(ctx, table_name, columns) 2188 2189 def _get(self, ctx, table_name, record_id, column_keys, 2190 id_=None, if_exists=False): 2191 vsctl_table = self._get_table(table_name) 2192 ovsrec_row = ctx.must_get_row(vsctl_table, record_id) 2193 2194 # TODO: Support symbol name 2195 # if id_: 2196 # symbol, new = ctx.create_symbol(id_) 2197 # if not new: 2198 # vsctl_fatal('row id "%s" specified on "get" command was ' 2199 # 'used before it was defined' % id_) 2200 # symbol.uuid = row.uuid 2201 # symbol.strong_ref = True 2202 2203 result = [] 2204 for column, key in column_keys: 2205 result.append(ctx.get_column(ovsrec_row, column, key, if_exists)) 2206 2207 return result 2208 2209 def _cmd_get(self, ctx, command): 2210 id_ = None # TODO: Support --id option 2211 if_exists = command.has_option('--if-exists') 2212 table_name = command.args[0] 2213 record_id = command.args[1] 2214 2215 column_keys = [ 2216 ctx.parse_column_key(column_key) 2217 for column_key in command.args[2:]] 2218 2219 command.result = self._get( 2220 ctx, table_name, record_id, column_keys, id_, if_exists) 2221 2222 def _check_mutable(self, table_name, column): 2223 column_schema = self.schema.tables[table_name].columns[column] 2224 if not column_schema.mutable: 2225 vsctl_fatal('cannot modify read-only column %s in table %s' % 2226 (column, table_name)) 2227 2228 def _pre_mod_columns(self, ctx, table_name, columns): 2229 self._pre_get_table(ctx, table_name) 2230 for column in columns: 2231 self._pre_get_column(ctx, table_name, column) 2232 self._check_mutable(table_name, column) 2233 2234 def _pre_cmd_set(self, ctx, command): 2235 table_name = command.args[0] 2236 table_schema = self.schema.tables[table_name] 2237 columns = [ 2238 ctx.parse_column_key_value(table_schema, column_key_value)[0] 2239 for column_key_value in command.args[2:]] 2240 2241 self._pre_mod_columns(ctx, table_name, columns) 2242 2243 def _set(self, ctx, table_name, record_id, column_values): 2244 """ 2245 :type column_values: list of (column, value_json) 2246 """ 2247 vsctl_table = self._get_table(table_name) 2248 ovsrec_row = ctx.must_get_row(vsctl_table, record_id) 2249 for column, value in column_values: 2250 ctx.set_column(ovsrec_row, column, value) 2251 ctx.invalidate_cache() 2252 2253 def _cmd_set(self, ctx, command): 2254 table_name = command.args[0] 2255 record_id = command.args[1] 2256 2257 # column_key_value: <column>[:<key>]=<value> 2258 table_schema = self.schema.tables[table_name] 2259 column_values = [ 2260 ctx.parse_column_key_value(table_schema, column_key_value) 2261 for column_key_value in command.args[2:]] 2262 2263 self._set(ctx, table_name, record_id, column_values) 2264 2265 def _pre_cmd_add(self, ctx, command): 2266 table_name = command.args[0] 2267 columns = [command.args[2]] 2268 2269 self._pre_mod_columns(ctx, table_name, columns) 2270 2271 def _add(self, ctx, table_name, record_id, column_values): 2272 """ 2273 :type column_values: list of (column, value_json) 2274 """ 2275 vsctl_table = self._get_table(table_name) 2276 ovsrec_row = ctx.must_get_row(vsctl_table, record_id) 2277 for column, value in column_values: 2278 ctx.add_column(ovsrec_row, column, value) 2279 ctx.invalidate_cache() 2280 2281 def _cmd_add(self, ctx, command): 2282 table_name = command.args[0] 2283 record_id = command.args[1] 2284 column = command.args[2] 2285 2286 column_key_value_strings = [] 2287 for value in command.args[3:]: 2288 if '=' in value: 2289 # construct <column>:<key>=value 2290 column_key_value_strings.append('%s:%s' % (column, value)) 2291 else: 2292 # construct <column>=value 2293 column_key_value_strings.append('%s=%s' % (column, value)) 2294 2295 table_schema = self.schema.tables[table_name] 2296 column_values = [ 2297 ctx.parse_column_key_value(table_schema, column_key_value_string) 2298 for column_key_value_string in column_key_value_strings] 2299 2300 self._add(ctx, table_name, record_id, column_values) 2301 2302 def _pre_cmd_remove(self, ctx, command): 2303 table_name = command.args[0] 2304 columns = [command.args[2]] 2305 2306 self._pre_mod_columns(ctx, table_name, columns) 2307 2308 def _remove(self, ctx, table_name, record_id, column_values): 2309 """ 2310 :type column_values: list of (column, value_json) 2311 """ 2312 vsctl_table = self._get_table(table_name) 2313 ovsrec_row = ctx.must_get_row(vsctl_table, record_id) 2314 for column, value in column_values: 2315 ctx.remove_column(ovsrec_row, column, value) 2316 ctx.invalidate_cache() 2317 2318 def _cmd_remove(self, ctx, command): 2319 table_name = command.args[0] 2320 record_id = command.args[1] 2321 column = command.args[2] 2322 2323 column_key_value_strings = [] 2324 for value in command.args[3:]: 2325 if '=' in value: 2326 # construct <column>:<key>=value 2327 column_key_value_strings.append('%s:%s' % (column, value)) 2328 else: 2329 # construct <column>=value 2330 column_key_value_strings.append('%s=%s' % (column, value)) 2331 2332 table_schema = self.schema.tables[table_name] 2333 column_values = [ 2334 ctx.parse_column_key_value(table_schema, column_key_value_string) 2335 for column_key_value_string in column_key_value_strings] 2336 2337 self._remove(ctx, table_name, record_id, column_values) 2338 2339 def _pre_cmd_clear(self, ctx, command): 2340 table_name = command.args[0] 2341 column = command.args[2] 2342 self._pre_mod_columns(ctx, table_name, [column]) 2343 2344 def _clear(self, ctx, table_name, record_id, column): 2345 vsctl_table = self._get_table(table_name) 2346 ovsrec_row = ctx.must_get_row(vsctl_table, record_id) 2347 column_schema = ctx.idl.tables[table_name].columns[column] 2348 if column_schema.type.n_min > 0: 2349 vsctl_fatal('"clear" operation cannot be applied to column %s ' 2350 'of table %s, which is not allowed to be empty' % 2351 (column, table_name)) 2352 2353 # assuming that default datum is empty. 2354 default_datum = ovs.db.data.Datum.default(column_schema.type) 2355 setattr(ovsrec_row, column, 2356 default_datum.to_python(ovs.db.idl._uuid_to_row)) 2357 ctx.invalidate_cache() 2358 2359 def _cmd_clear(self, ctx, command): 2360 table_name = command.args[0] 2361 record_id = command.args[1] 2362 column = command.args[2] 2363 self._clear(ctx, table_name, record_id, column) 2364 2365 2366# 2367# Create constants from ovs db schema 2368# 2369 2370def schema_print(schema_location, prefix): 2371 prefix = prefix.upper() 2372 2373 json = ovs.json.from_file(schema_location) 2374 schema = ovs.db.schema.DbSchema.from_json(json) 2375 2376 print('# Do NOT edit.') 2377 print('# This is automatically generated by %s' % __file__) 2378 print('# created based on version %s' % (schema.version or 'unknown')) 2379 print('') 2380 print('') 2381 print('%s_DB_NAME = \'%s\'' % (prefix, schema.name)) 2382 for table in sorted(schema.tables.values(), 2383 key=operator.attrgetter('name')): 2384 print('') 2385 print('%s_TABLE_%s = \'%s\'' % (prefix, 2386 table.name.upper(), table.name)) 2387 for column in sorted(table.columns.values(), 2388 key=operator.attrgetter('name')): 2389 print('%s_%s_COL_%s = \'%s\'' % (prefix, table.name.upper(), 2390 column.name.upper(), 2391 column.name)) 2392 2393 2394def main(): 2395 if len(sys.argv) <= 2: 2396 print('Usage: %s <schema file>' % sys.argv[0]) 2397 print('e.g.: %s vswitchd/vswitch.ovsschema' % sys.argv[0]) 2398 2399 location = sys.argv[1] 2400 prefix = 'OVSREC' 2401 schema_print(location, prefix) 2402 2403 2404if __name__ == '__main__': 2405 main() 2406