diff options
author | Isaku Yamahata <yamahata@valinux.co.jp> | 2013-02-08 12:28:07 +0900 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2013-02-08 22:55:16 +0900 |
commit | f8a99a3be27a6f3d79eceb96fedfee4826a93dca (patch) | |
tree | b632b774b6e2fb35c7ae7098e8c0de1888290bc5 | |
parent | 966099ce34e09149f3b57bfac32c2020455de5e8 (diff) |
ovs/vsctl: more commands
Signed-off-by: Isaku Yamahata <yamahata@valinux.co.jp>
Signed-off-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
-rw-r--r-- | ryu/lib/ovs/vsctl.py | 393 |
1 files changed, 393 insertions, 0 deletions
diff --git a/ryu/lib/ovs/vsctl.py b/ryu/lib/ovs/vsctl.py index 19bc067e..bda1dd62 100644 --- a/ryu/lib/ovs/vsctl.py +++ b/ryu/lib/ovs/vsctl.py @@ -18,7 +18,9 @@ import gevent import itertools import logging +import operator import os +import sys import weakref import ovs.db.data @@ -213,6 +215,21 @@ class VSCtlContext(object): self.bridges[name] = vsctl_bridge return vsctl_bridge + def del_cached_bridge(self, vsctl_bridge): + assert not vsctl_bridge.ports + assert not vsctl_bridge.children + + parent = vsctl_bridge.parent + if parent: + parent.children.remove(vsctl_bridge) + vsctl_bridge.parent = None # break circular reference + ovsrec_bridge = vsctl_bridge.br_cfg + if ovsrec_bridge: + ovsrec_bridge.delete() + self.ovs_delete_bridge(ovsrec_bridge) + + del self.bridges[vsctl_bridge.name] + def add_port_to_cache(self, vsctl_bridge_parent, ovsrec_port): tag = getattr(ovsrec_port, vswitch_idl.OVSREC_PORT_COL_TAG, None) if (tag is not None and tag >= 0 and tag < 4096): @@ -428,6 +445,16 @@ class VSCtlContext(object): vswitch_idl.OVSREC_BRIDGE_COL_PORTS, ovsrec_port) + def ovs_insert_bridge(self, ovsrec_bridge): + self._column_insert(self.ovs, + vswitch_idl.OVSREC_OPEN_VSWITCH_COL_BRIDGES, + ovsrec_bridge) + + def ovs_delete_bridge(self, ovsrec_bridge): + self._column_delete(self.ovs, + vswitch_idl.OVSREC_OPEN_VSWITCH_COL_BRIDGES, + ovsrec_bridge) + def del_port(self, vsctl_port): if vsctl_port.bridge().parent: ovsrec_bridge = vsctl_port.bridge().parent.br_cfg @@ -439,6 +466,13 @@ class VSCtlContext(object): self.del_cached_iface(vsctl_iface) self.del_cached_port(vsctl_port) + def del_bridge(self, vsctl_bridge): + for child in vsctl_bridge.children.copy(): + self.del_bridge(child) + for vsctl_port in vsctl_bridge.ports.copy(): + self.del_port(vsctl_port) + self.del_cached_bridge(vsctl_bridge) + def add_port(self, br_name, port_name, may_exist, fake_iface, iface_names, settings=None): """ @@ -504,6 +538,77 @@ class VSCtlContext(object): for ovsrec_iface in ifaces: self.add_iface_to_cache(vsctl_port, ovsrec_iface) + def add_bridge(self, br_name, parent_name=None, vlan=0, may_exist=False): + self.populate_cache() + if may_exist: + vsctl_bridge = self.find_bridge(br_name, False) + if vsctl_bridge: + if not parent_name: + if vsctl_bridge.parent: + vsctl_fatal('"--may-exist add-vsctl_bridge %s" ' + 'but %s is a VLAN bridge for VLAN %d' % + (br_name, br_name, vsctl_bridge.vlan)) + else: + if not vsctl_bridge.parent: + vsctl_fatal('"--may-exist add-vsctl_bridge %s %s %d" ' + 'but %s is not a VLAN bridge' % + (br_name, parent_name, vlan, br_name)) + elif vsctl_bridge.parent.name != parent_name: + vsctl_fatal('"--may-exist add-vsctl_bridge %s %s %d" ' + 'but %s has the wrong parent %s' % + (br_name, parent_name, vlan, + br_name, vsctl_bridge.parent.name)) + elif vsctl_bridge.vlan != vlan: + vsctl_fatal('"--may-exist add-vsctl_bridge %s %s %d" ' + 'but %s is a VLAN bridge for the wrong ' + 'VLAN %d' % + (br_name, parent_name, vlan, br_name, + vsctl_bridge.vlan)) + return + + self.check_conflicts(br_name, + 'cannot create a bridge named %s' % br_name) + + txn = self.txn + tables = self.idl.tables + if not parent_name: + ovsrec_iface = txn.insert( + tables[vswitch_idl.OVSREC_TABLE_INTERFACE]) + ovsrec_iface.name = br_name + ovsrec_iface.type = 'internal' + + ovsrec_port = txn.insert(tables[vswitch_idl.OVSREC_TABLE_PORT]) + ovsrec_port.name = br_name + ovsrec_port.interfaces = [ovsrec_iface] + ovsrec_port.fake_bridge = False + + ovsrec_bridge = txn.insert(tables[vswitch_idl.OVSREC_TABLE_BRIDGE]) + ovsrec_bridge.name = br_name + ovsrec_bridge.ports = [ovsrec_port] + + self.ovs_insert_bridge(ovsrec_bridge) + else: + parent = self.find_bridge(parent_name, False) + if parent and parent.parent: + vsctl_fatal('cannot create bridge with fake bridge as parent') + if not parent: + vsctl_fatal('parent bridge %s does not exist' % parent_name) + + ovsrec_iface = txn.insert( + tables[vswitch_idl.OVSREC_TABLE_INTERFACE]) + ovsrec_iface.name = br_name + ovsrec_iface.type = 'internal' + + ovsrec_port = txn.insert(tables[vswitch_idl.OVSREC_TABLE_PORT]) + ovsrec_port.name = br_name + ovsrec_port.interfaces = [ovsrec_iface] + ovsrec_port.fake_bridge = True + ovsrec_port.tag = [vlan] + + self.bridge_insert_port(parent.br_cfg, ovsrec_port) + + self.invalidate_cache() + @staticmethod def parse_column_key_value(table_schema, setting_string): """ @@ -615,6 +720,15 @@ class VSCtlContext(object): return ovsrec_row +class _CmdShowTable(object): + def __init__(self, table, name_column, columns, recurse): + super(_CmdShowTable, self).__init__() + self.table = table + self.name_column = name_column + self.columns = columns + self.recurse = recurse + + class _VSCtlRowID(object): def __init__(self, table, name_column, uuid_column): super(_VSCtlRowID, self).__init__() @@ -826,19 +940,53 @@ class VSCtl(object): :type commands: list of VSCtlCommand """ all_commands = { + # Open vSwitch commands. + 'init': (None, self._cmd_init), + 'show': (self._pre_cmd_show, self._cmd_show), + + # Bridge commands. + 'add-br': (self._pre_add_br, self._cmd_add_br), + 'del-br': (self._pre_get_info, self._cmd_del_br), + 'list-br': (self._pre_get_info, self._cmd_list_br), + # Port. commands 'list-ports': (self._pre_get_info, self._cmd_list_ports), 'add-port': (self._pre_cmd_add_port, self._cmd_add_port), 'del-port': (self._pre_get_info, self._cmd_del_port), + # 'add-bond': + # 'port-to-br': + + # Interface commands. + 'list-ifaces': (self._pre_get_info, self._cmd_list_ifaces), + # 'iface-to-br': # Controller commands. + 'get-controller': (self._pre_controller, self._cmd_get_controller), 'del-controller': (self._pre_controller, self._cmd_del_controller), 'set-controller': (self._pre_controller, self._cmd_set_controller), + # 'get-fail-mode': + # 'del-fail-mode': + # 'set-fail-mode': + + # Manager commands. + # 'get-manager': + # 'del-manager': + # 'set-manager': + + # Switch commands. + # 'emer-reset': # Database commands. + # 'comment': 'get': (self._pre_cmd_get, self._cmd_get), + # 'list': 'find': (self._pre_cmd_find, self._cmd_find), 'set': (self._pre_cmd_set, self._cmd_set), + # 'add': + 'clear': (self._pre_cmd_clear, self._cmd_clear), + # 'create': + # 'destroy': + # 'wait-until': # for quantum_adapter 'list-ifaces-verbose': (self._pre_cmd_list_ifaces_verbose, @@ -857,6 +1005,112 @@ class VSCtl(object): with gevent.Timeout(timeout_msec * 1000, exception): self._run_command(commands) + # commands + def _cmd_init(self, _ctx, _command): + # nothing. Just check connection to ovsdb + pass + + _CMD_SHOW_TABLES = [ + _CmdShowTable(vswitch_idl.OVSREC_TABLE_OPEN_VSWITCH, None, + [vswitch_idl.OVSREC_OPEN_VSWITCH_COL_MANAGER_OPTIONS, + vswitch_idl.OVSREC_OPEN_VSWITCH_COL_BRIDGES, + vswitch_idl.OVSREC_OPEN_VSWITCH_COL_OVS_VERSION], + False), + _CmdShowTable(vswitch_idl.OVSREC_TABLE_BRIDGE, + vswitch_idl.OVSREC_BRIDGE_COL_NAME, + [vswitch_idl.OVSREC_BRIDGE_COL_CONTROLLER, + vswitch_idl.OVSREC_BRIDGE_COL_FAIL_MODE, + vswitch_idl.OVSREC_BRIDGE_COL_PORTS], + False), + _CmdShowTable(vswitch_idl.OVSREC_TABLE_PORT, + vswitch_idl.OVSREC_PORT_COL_NAME, + [vswitch_idl.OVSREC_PORT_COL_TAG, + vswitch_idl.OVSREC_PORT_COL_TRUNKS, + vswitch_idl.OVSREC_PORT_COL_INTERFACES], + False), + _CmdShowTable(vswitch_idl.OVSREC_TABLE_INTERFACE, + vswitch_idl.OVSREC_INTERFACE_COL_NAME, + [vswitch_idl.OVSREC_INTERFACE_COL_TYPE, + vswitch_idl.OVSREC_INTERFACE_COL_OPTIONS], + False), + _CmdShowTable(vswitch_idl.OVSREC_TABLE_CONTROLLER, + vswitch_idl.OVSREC_CONTROLLER_COL_TARGET, + [vswitch_idl.OVSREC_CONTROLLER_COL_IS_CONNECTED], + False), + _CmdShowTable(vswitch_idl.OVSREC_TABLE_MANAGER, + vswitch_idl.OVSREC_MANAGER_COL_TARGET, + [vswitch_idl.OVSREC_MANAGER_COL_IS_CONNECTED], + False), + ] + + def _pre_cmd_show(self, _ctx, _command): + schema_helper = self.schema_helper + for show in self._CMD_SHOW_TABLES: + schema_helper.register_table(show.table) + if show.name_column: + schema_helper.register_columns(show.table, [show.name_column]) + schema_helper.register_columns(show.table, show.columns) + + @staticmethod + def _cmd_show_find_table_by_row(row): + for show in VSCtl._CMD_SHOW_TABLES: + if show.table == row._table.name: + return show + return None + + @staticmethod + def _cmd_show_find_table_by_name(name): + for show in VSCtl._CMD_SHOW_TABLES: + if show.table == name: + return show + return None + + @staticmethod + def _cmd_show_row(ctx, row, level): + _INDENT_SIZE = 4 # # of spaces per indent + show = VSCtl._cmd_show_find_table_by_row(row) + output = '' + + output += ' ' * level * _INDENT_SIZE + if show and show.name_column: + output += '%s ' % show.table + datum = getattr(row, show.name_column) + output += datum + else: + output += str(row.uuid) + output += '\n' + + if not show or show.recurse: + return + + show.recurse = True + for column in show.columns: + datum = row._data[column] + key = datum.type.key + if (key.type == ovs.db.types.UuidType and key.ref_table_name): + ref_show = VSCtl._cmd_show_find_table_by_name( + key.ref_table_name) + if ref_show: + for atom in datum.values: + ref_row = ctx.idl.tables[ref_show.table].rows.get( + atom.value) + if ref_row: + VSCtl._cmd_show_row(ctx, ref_row, level + 1) + continue + + if not datum.is_default(): + output += ' ' * (level + 1) * _INDENT_SIZE + output += '%s: %s\n' % (column, datum) + + show.recurse = False + return output + + def _cmd_show(self, ctx, command): + for row in ctx.idl.tables[ + self._CMD_SHOW_TABLES[0].table].rows.values(): + output = self._cmd_show_row(ctx, row, 0) + command.result = output + def _pre_get_info(self, _ctx, _command): schema_helper = self.schema_helper @@ -879,6 +1133,43 @@ class VSCtl(object): vswitch_idl.OVSREC_TABLE_INTERFACE, [vswitch_idl.OVSREC_INTERFACE_COL_NAME]) + def _cmd_list_br(self, ctx, command): + ctx.populate_cache() + command.result = sorted(ctx.bridges.keys()) + + def _pre_add_br(self, ctx, command): + self._pre_get_info(ctx, command) + + schema_helper = self.schema_helper + schema_helper.register_columns( + vswitch_idl.OVSREC_TABLE_INTERFACE, + [vswitch_idl.OVSREC_INTERFACE_COL_TYPE]) + + def _cmd_add_br(self, ctx, command): + br_name = command.args[0] + if len(command.args) == 1: + parent_name = None + vlan = 0 + elif len(command.args) == 3: + parent_name = command.args[1] + vlan = int(command.args[2]) + if vlan < 0 or vlan > 4095: + vsctl_fatal("vlan must be between 0 and 4095 %d" % vlan) + else: + vsctl_fatal('this command takes exactly 1 or 3 argument') + + ctx.add_bridge(br_name, parent_name, vlan) + + def _del_br(self, ctx, br_name, must_exist=False): + ctx.populate_cache() + br = ctx.find_bridge(br_name, must_exist) + if br: + ctx.del_bridge(br) + + def _cmd_del_br(self, ctx, command): + br_name = command.args[0] + self._del_br(ctx, br_name) + def _list_ports(self, ctx, br_name): ctx.populate_cache() br = ctx.find_bridge(br_name, True) @@ -962,6 +1253,25 @@ class VSCtl(object): br_name = command.args[0] if len(command.args) == 2 else None self._del_port(ctx, br_name, target, must_exist, with_iface) + def _list_ifaces(self, ctx, br_name): + ctx.populate_cache() + + br = ctx.find_bridge(br_name, True) + ctx.verify_ports() + + iface_names = set() + for vsctl_port in br.ports: + for vsctl_iface in vsctl_port.ifaces: + iface_name = vsctl_iface.iface_cfg.name + if iface_name != br_name: + iface_names.add(iface_name) + return iface_names + + def _cmd_list_ifaces(self, ctx, command): + br_name = command.args[0] + iface_names = self._list_ifaces(ctx, br_name) + command.result = sorted(iface_names) + def _pre_cmd_list_ifaces_verbose(self, ctx, command): self._pre_get_info(ctx, command) schema_helper = self.schema_helper @@ -1028,6 +1338,17 @@ class VSCtl(object): vswitch_idl.OVSREC_TABLE_CONTROLLER, [vswitch_idl.OVSREC_CONTROLLER_COL_TARGET]) + def _get_controller(self, ctx, br_name): + ctx.populate_cache() + br = ctx.find_bridge(br_name, True) + self._verify_controllers(br.br_cfg) + return set(controller.target for controller in br.br_cfg.controller) + + def _cmd_get_controller(self, ctx, command): + br_name = command.args[0] + controller_names = self._get_controller(ctx, br_name) + command.result = sorted(controller_names) + def _delete_controllers(self, ovsrec_controllers): for controller in ovsrec_controllers: controller.delete() @@ -1339,3 +1660,75 @@ class VSCtl(object): for column_key_value in command.args[2:]] self._set(ctx, table_name, record_id, column_key_values) + + def _pre_clear(self, ctx, table_name, column): + self._pre_get_table(ctx, table_name) + self._pre_get_column(ctx, table_name, column) + self._check_mutable(table_name, column) + + def _pre_cmd_clear(self, ctx, command): + table_name = command.args[0] + column = command.args[2] + self._pre_clear(ctx, table_name, column) + + def _clear(self, ctx, table_name, record_id, column): + vsctl_table = self._get_table(table_name) + ovsrec_row = ctx.must_get_row(vsctl_table, record_id) + column_schema = ctx.idl.tables[table_name].columns[column] + if column_schema.type.n_min > 0: + vsctl_fatal('"clear" operation cannot be applied to column %s ' + 'of table %s, which is not allowed to be empty' % + (column, table_name)) + + # assuming that default datum is empty. + default_datum = ovs.db.data.Datum.default(column_schema.type) + setattr(ovsrec_row, column, + default_datum.to_python(ovs.db.idl._uuid_to_row)) + ctx.invalidate_cache() + + def _cmd_clear(self, ctx, command): + table_name = command.args[0] + record_id = command.args[1] + column = command.args[2] + self._clear(ctx, table_name, record_id, column) + + +# +# Create constants from ovs db schema +# + +def schema_print(schema_location, prefix): + prefix = prefix.upper() + + json = ovs.json.from_file(schema_location) + schema = ovs.db.schema.DbSchema.from_json(json) + + print '# Do NOT edit.' + print '# This is automatically generated.' + print '# created based on version %s' % (schema.version or 'unknown') + print '' + print '' + print '%s_DB_NAME = \'%s\'' % (prefix, schema.name) + for table in sorted(schema.tables.values(), + key=operator.attrgetter('name')): + print '' + print '%s_TABLE_%s = \'%s\'' % (prefix, + table.name.upper(), table.name) + for column in sorted(table.columns.values(), + key=operator.attrgetter('name')): + print '%s_%s_COL_%s = \'%s\'' % (prefix, table.name.upper(), + column.name.upper(), + column.name) + + +def main(): + if len(sys.argv) <= 2: + print 'Usage: %s <schema file> <prefix>' % sys.argv[0] + + location = sys.argv[1] + prefix = sys.argv[2] + schema_print(location, prefix) + + +if __name__ == '__main__': + main() |