From: Ben Pfaff Date: Wed, 9 Dec 2009 01:18:12 +0000 (-0800) Subject: vsctl: Start making it work with ovsdb. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c75d15113c56e30ea5a1ace777cd3105c7b750d3;p=openvswitch vsctl: Start making it work with ovsdb. This passes at least one test (the one named "add-br a"). It probably doesn't pass any more than that. This is *way* not up to my quality standards, but we are in a super hurry so I'm pushing it anyhow. --- diff --git a/lib/vlog-modules.def b/lib/vlog-modules.def index 9a9ea065..3a4f92ab 100644 --- a/lib/vlog-modules.def +++ b/lib/vlog-modules.def @@ -91,6 +91,7 @@ VLOG_MODULE(vconn_ssl) VLOG_MODULE(vconn_stream) VLOG_MODULE(vconn_unix) VLOG_MODULE(vconn) +VLOG_MODULE(vsctl) VLOG_MODULE(vlog) VLOG_MODULE(wcelim) VLOG_MODULE(vswitchd) diff --git a/tests/ovs-vsctl.at b/tests/ovs-vsctl.at index 85812c44..ada514f7 100644 --- a/tests/ovs-vsctl.at +++ b/tests/ovs-vsctl.at @@ -1,20 +1,36 @@ +dnl OVS_VSCTL_SETUP +dnl +dnl Creates an empty database in the current directory and then starts +dnl an ovsdb-server on it for ovs-vsctl to connect to. +m4_define([OVS_VSCTL_SETUP], + [OVS_CHECK_LCOV( + [ovsdb-tool create db $abs_builddir/../vswitchd/vswitch-idl.ovsschema], + [0], [stdout], [ignore]) + OVS_CHECK_LCOV( + [[ovsdb-tool transact db \ + '[{"op": "insert", + "table": "Open_vSwitch", + "row": {}}]']], + [0], [ignore], [ignore]) + AT_CHECK([ovsdb-server --detach --pidfile=$PWD/pid --listen=punix:socket --unixctl=$PWD/unixctl db >/dev/null 2>&1], [0], [ignore], [ignore])]) + +dnl OVS_VSCTL_CLEANUP +dnl +dnl Kills off the database server. +m4_define([OVS_VSCTL_CLEANUP], [test ! -e pid || kill `cat pid`]) + dnl RUN_OVS_VSCTL(COMMAND, ...) dnl -dnl Executes each ovs-vsctl COMMAND on a file named "conf" in the -dnl current directory. Creates "conf" if it does not already exist. +dnl Executes each ovs-vsctl COMMAND. m4_define([RUN_OVS_VSCTL], - [: >> conf -m4_foreach([command], [$@], [ovs-vsctl --no-reload --config=conf command + [m4_foreach([command], [$@], [ovs-vsctl -vreconnect:ANY:emer --db=unix:socket command ])]) dnl RUN_OVS_VSCTL_TOGETHER(COMMAND, ...) dnl -dnl Executes each ovs-vsctl COMMAND on a file named "conf" in the -dnl current directory, in a single run of ovs-vsctl. Creates "conf" if it -dnl does not already exist. +dnl Executes each ovs-vsctl COMMAND in a single run of ovs-vsctl. m4_define([RUN_OVS_VSCTL_TOGETHER], - [: >> conf - ovs-vsctl --no-reload --config=conf m4_join([ -- ], $@)]) + [ovs-vsctl -vreconnect:ANY:emer --db=unix:socket m4_join([ -- ], $@)]) dnl CHECK_BRIDGES([BRIDGE, PARENT, VLAN], ...) dnl @@ -23,43 +39,49 @@ dnl which must be in alphabetical order. Also checks that each BRIDGE has the dnl specified PARENT and is on the given VLAN. m4_define([_CHECK_BRIDGE], [AT_CHECK([RUN_OVS_VSCTL([br-to-parent $1])], [0], [$2 -]) +], [], [OVS_VSCTL_CLEANUP]) # Check br-to-vlan, without --oneline. AT_CHECK([RUN_OVS_VSCTL([br-to-vlan $1])], [0], [$3 -]) +], [], [OVS_VSCTL_CLEANUP]) # Check br-to-vlan, with --oneline. # (This particular test is interesting with --oneline because it returns # an integer instead of a string and that can cause type mismatches inside # python if not done carefully.) AT_CHECK([RUN_OVS_VSCTL([--oneline br-to-vlan $1])], [0], [$3 -]) +], [], [OVS_VSCTL_CLEANUP]) # Check multiple queries in a single run. AT_CHECK([RUN_OVS_VSCTL_TOGETHER([br-to-parent $1], [br-to-vlan $1])], [0], [$2 $3 -])]) +], [], [OVS_VSCTL_CLEANUP])]) m4_define([CHECK_BRIDGES], [dnl Check that the bridges appear on list-br, without --oneline. AT_CHECK( [RUN_OVS_VSCTL([list-br])], [0], [m4_foreach([brinfo], [$@], [m4_car(brinfo) -])]) +])], + [], + [OVS_VSCTL_CLEANUP]) dnl Check that the bridges appear on list-br, with --oneline. AT_CHECK( [RUN_OVS_VSCTL([--oneline list-br])], [0], [m4_join([\n], m4_foreach([brinfo], [$@], [m4_car(brinfo),])) -]) +], + [], + [OVS_VSCTL_CLEANUP]) dnl Check that each bridge exists according to br-exists and that dnl a bridge that should not exist does not. m4_foreach([brinfo], [$@], - [AT_CHECK([RUN_OVS_VSCTL([br-exists m4_car(brinfo)])])]) - AT_CHECK([RUN_OVS_VSCTL([br-exists nonexistent])], [2]) + [AT_CHECK([RUN_OVS_VSCTL([br-exists m4_car(brinfo)])], [0], [], + [], [OVS_VSCTL_CLEANUP])]) + AT_CHECK([RUN_OVS_VSCTL([br-exists nonexistent])], [2], [], [], + [OVS_VSCTL_CLEANUP]) dnl Check that each bridge has the expected parent and VLAN. m4_map([_CHECK_BRIDGE], [$@])]) @@ -76,20 +98,26 @@ m4_define([CHECK_PORTS], [RUN_OVS_VSCTL([list-ports $1])], [0], [m4_foreach([port], m4_cdr($@), [port -])]) +])], + [], + [OVS_VSCTL_CLEANUP]) dnl Check ports with --oneline. AT_CHECK( [RUN_OVS_VSCTL([--oneline list-ports $1])], [0], [m4_join([\n], m4_shift($@)) -]) - AT_CHECK([RUN_OVS_VSCTL([port-to-br $1])], [1], [], [ovs-vsctl: no port named $1 -]) +], + [], + [OVS_VSCTL_CLEANUP]) + AT_CHECK([RUN_OVS_VSCTL([port-to-br $1])], [1], [], + [ovs-vsctl: no port named $1 +], + [OVS_VSCTL_CLEANUP]) m4_foreach( [port], m4_cdr($@), [AT_CHECK([RUN_OVS_VSCTL([[port-to-br] port])], [0], [$1 -])])]) +], [], [OVS_VSCTL_CLEANUP])])]) dnl CHECK_IFACES(BRIDGE, IFACE[, IFACE...]) dnl @@ -103,157 +131,145 @@ m4_define([CHECK_IFACES], [0], [m4_foreach([iface], m4_cdr($@), [iface ])]) - AT_CHECK([RUN_OVS_VSCTL([iface-to-br $1])], [1], [], [ovs-vsctl: no interface named $1 -]) + AT_CHECK([RUN_OVS_VSCTL([iface-to-br $1])], [1], [], + [ovs-vsctl: no interface named $1 +], + [OVS_VSCTL_CLEANUP]) m4_foreach( [iface], m4_cdr($@), [AT_CHECK([RUN_OVS_VSCTL([[iface-to-br] iface])], [0], [$1 -])])]) +], + [], [OVS_VSCTL_CLEANUP])])]) dnl ---------------------------------------------------------------------- AT_BANNER([ovs-vsctl unit tests -- real bridges]) AT_SETUP([add-br a]) AT_KEYWORDS([ovs-vsctl]) -AT_CHECK([RUN_OVS_VSCTL([add-br a])]) -AT_CHECK([cat conf], [0], [dnl -bridge.a.port=a -]) +OVS_VSCTL_SETUP +AT_CHECK([RUN_OVS_VSCTL([add-br a])], [0], [], [], [OVS_VSCTL_CLEANUP]) CHECK_BRIDGES([a, a, 0]) CHECK_PORTS([a]) CHECK_IFACES([a]) +OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([add-br a, add-br a]) AT_KEYWORDS([ovs-vsctl]) -AT_CHECK([RUN_OVS_VSCTL([add-br a])]) +OVS_VSCTL_SETUP +AT_CHECK([RUN_OVS_VSCTL([add-br a])], [0], [], [], [OVS_VSCTL_CLEANUP]) AT_CHECK([RUN_OVS_VSCTL([add-br a])], [1], [], [ovs-vsctl: cannot create a bridge named a because a bridge named a already exists -]) +], [OVS_VSCTL_CLEANUP]) +OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([add-br a, add-br b]) AT_KEYWORDS([ovs-vsctl]) -AT_CHECK([RUN_OVS_VSCTL([add-br a], [add-br b])]) -AT_CHECK([cat conf], [0], [dnl -bridge.a.port=a -bridge.b.port=b -]) +OVS_VSCTL_SETUP +AT_CHECK([RUN_OVS_VSCTL([add-br a], [add-br b])], [0], [], [], + [OVS_VSCTL_CLEANUP]) CHECK_BRIDGES([a, a, 0], [b, b, 0]) CHECK_PORTS([a]) CHECK_IFACES([a]) CHECK_PORTS([b]) CHECK_IFACES([b]) +OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([add-br a, add-br b, del-br a]) AT_KEYWORDS([ovs-vsctl]) -AT_CHECK([RUN_OVS_VSCTL([add-br a], [add-br b], [del-br a])]) -AT_CHECK([cat conf], [0], [dnl -bridge.b.port=b -]) +OVS_VSCTL_SETUP +AT_CHECK([RUN_OVS_VSCTL([add-br a], [add-br b], [del-br a])], [0], [], [], + [OVS_VSCTL_CLEANUP]) CHECK_BRIDGES([b, b, 0]) CHECK_PORTS([b]) CHECK_IFACES([b]) +OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([add-br a, add-port a a1, add-port a a2]) AT_KEYWORDS([ovs-vsctl]) +OVS_VSCTL_SETUP AT_CHECK([RUN_OVS_VSCTL( [add-br a], [add-port a a1], - [add-port a a2])]) -AT_CHECK([cat conf], [0], - [bridge.a.port=a -bridge.a.port=a1 -bridge.a.port=a2 -]) + [add-port a a2])], [0], [], [], [OVS_VSCTL_CLEANUP]) CHECK_BRIDGES([a, a, 0]) CHECK_PORTS([a], [a1], [a2]) CHECK_IFACES([a], [a1], [a2]) +OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([add-br a, add-port a a1, add-port a a1]) AT_KEYWORDS([ovs-vsctl]) +OVS_VSCTL_SETUP AT_CHECK([RUN_OVS_VSCTL( [add-br a], - [add-port a a1])]) -AT_CHECK([cat conf], [0], - [bridge.a.port=a -bridge.a.port=a1 -]) + [add-port a a1])], [0], [], [], [OVS_VSCTL_CLEANUP]) AT_CHECK([RUN_OVS_VSCTL([add-port a a1])], [1], [], [ovs-vsctl: cannot create a port named a1 because a port named a1 already exists on bridge a -]) +], [OVS_VSCTL_CLEANUP]) +OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([add-br a b, add-port a a1, add-port b b1, del-br a]) AT_KEYWORDS([ovs-vsctl]) +OVS_VSCTL_SETUP AT_CHECK([RUN_OVS_VSCTL_TOGETHER( [add-br a], [add-br b], [add-port a a1], [add-port b b1], - [del-br a])]) -AT_CHECK([cat conf], [0], - [bridge.b.port=b -bridge.b.port=b1 -]) + [del-br a])], [0], [], [], [OVS_VSCTL_CLEANUP]) CHECK_BRIDGES([b, b, 0]) CHECK_PORTS([b], [b1]) CHECK_IFACES([b], [b1]) +OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([add-br a, add-bond a bond0 a1 a2 a3]) AT_KEYWORDS([ovs-vsctl]) +OVS_VSCTL_SETUP AT_CHECK([RUN_OVS_VSCTL( [add-br a], - [add-bond a bond0 a1 a2 a3])]) -AT_CHECK([cat conf], [0], [dnl -bonding.bond0.slave=a1 -bonding.bond0.slave=a2 -bonding.bond0.slave=a3 -bridge.a.port=a -bridge.a.port=bond0 -]) + [add-bond a bond0 a1 a2 a3])], [0], [], [], [OVS_VSCTL_CLEANUP]) CHECK_BRIDGES([a, a, 0]) CHECK_PORTS([a], [bond0]) CHECK_IFACES([a], [a1], [a2], [a3]) +OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([add-br a b, add-port a a1, add-port b b1, del-port a a1]) AT_KEYWORDS([ovs-vsctl]) +OVS_VSCTL_SETUP AT_CHECK([RUN_OVS_VSCTL( [add-br a], [add-br b], [add-port a a1], [add-port b b1], - [del-port a a1])]) -AT_CHECK([cat conf], [0], [dnl -bridge.a.port=a -bridge.b.port=b -bridge.b.port=b1 -]) + [del-port a a1])], [0], [], [], [OVS_VSCTL_CLEANUP]) CHECK_BRIDGES([a, a, 0], [b, b, 0]) CHECK_PORTS([a]) CHECK_IFACES([a]) CHECK_PORTS([b], [b1]) CHECK_IFACES([b], [b1]) +OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([add-br a, add-bond a bond0 a1 a2 a3, del-port bond0]) AT_KEYWORDS([ovs-vsctl]) +OVS_VSCTL_SETUP AT_CHECK([RUN_OVS_VSCTL_TOGETHER( [add-br a], [add-bond a bond0 a1 a2 a3], - [del-port bond0])]) -AT_CHECK([cat conf], [0], [dnl -bridge.a.port=a -]) + [del-port bond0])], [0], [], [], [OVS_VSCTL_CLEANUP]) CHECK_BRIDGES([a, a, 0]) CHECK_PORTS([a]) +OVS_VSCTL_CLEANUP AT_CLEANUP +m4_define([ovs_vsctl_disabled_tests], [ dnl ---------------------------------------------------------------------- AT_BANNER([ovs-vsctl unit tests -- fake bridges]) @@ -270,6 +286,7 @@ vlan.xapi1.tag=9 AT_SETUP([simple fake bridge]) AT_KEYWORDS([ovs-vsctl fake-bridge]) +OVS_VSCTL_SETUP AT_CHECK([RUN_OVS_VSCTL( [add-br xenbr0], [add-port xenbr0 eth0], @@ -281,10 +298,12 @@ CHECK_PORTS([xenbr0], [eth0]) CHECK_IFACES([xenbr0], [eth0]) CHECK_PORTS([xapi1], [eth0.9]) CHECK_IFACES([xapi1], [eth0.9]) +OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([simple fake bridge + del-br fake bridge]) AT_KEYWORDS([ovs-vsctl fake-bridge]) +OVS_VSCTL_SETUP AT_DATA([conf], [SIMPLE_FAKE_CONF]) AT_CHECK([RUN_OVS_VSCTL([del-br xapi1])]) AT_CHECK([cat conf], [0], [dnl @@ -294,14 +313,17 @@ bridge.xenbr0.port=xenbr0 CHECK_BRIDGES([xenbr0, xenbr0, 0]) CHECK_PORTS([xenbr0], [eth0]) CHECK_IFACES([xenbr0], [eth0]) +OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([simple fake bridge + del-br real bridge]) AT_KEYWORDS([ovs-vsctl fake-bridge]) +OVS_VSCTL_SETUP AT_DATA([conf], [SIMPLE_FAKE_CONF]) AT_CHECK([RUN_OVS_VSCTL([del-br xenbr0])]) AT_CHECK([cat conf], [0], []) CHECK_BRIDGES +OVS_VSCTL_CLEANUP AT_CLEANUP m4_define([BOND_FAKE_CONF], [dnl @@ -319,6 +341,7 @@ vlan.xapi2.tag=11 AT_SETUP([fake bridge on bond]) AT_KEYWORDS([ovs-vsctl fake-bridge]) +OVS_VSCTL_SETUP AT_CHECK([RUN_OVS_VSCTL( [add-br xapi1], [add-bond xapi1 bond0 eth0 eth1], @@ -330,16 +353,19 @@ CHECK_PORTS([xapi1], [bond0]) CHECK_IFACES([xapi1], [eth0], [eth1]) CHECK_PORTS([xapi2], [bond0.11]) CHECK_IFACES([xapi2], [bond0.11]) +OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([fake bridge on bond + del-br fake bridge]) AT_KEYWORDS([ovs-vsctl fake-bridge]) +OVS_VSCTL_SETUP AT_DATA([conf], [BOND_FAKE_CONF]) AT_CHECK([RUN_OVS_VSCTL([--oneline del-br xapi2])], [0], [ ]) CHECK_BRIDGES([xapi1, xapi1, 0]) CHECK_PORTS([xapi1], [bond0]) CHECK_IFACES([xapi1], [eth0], [eth1]) +OVS_VSCTL_CLEANUP AT_CLEANUP AT_SETUP([fake bridge on bond + del-br real bridge]) @@ -347,4 +373,6 @@ AT_KEYWORDS([ovs-vsctl fake-bridge]) AT_DATA([conf], [BOND_FAKE_CONF]) AT_CHECK([RUN_OVS_VSCTL([del-br xapi1])]) CHECK_BRIDGES +OVS_VSCTL_CLEANUP AT_CLEANUP +]) diff --git a/utilities/automake.mk b/utilities/automake.mk index 2c57da02..aec78b53 100644 --- a/utilities/automake.mk +++ b/utilities/automake.mk @@ -6,6 +6,7 @@ bin_PROGRAMS += \ utilities/ovs-kill \ utilities/ovs-ofctl \ utilities/ovs-openflowd \ + utilities/ovs-vsctl \ utilities/ovs-wdt noinst_PROGRAMS += utilities/nlmon bin_SCRIPTS += utilities/ovs-pki utilities/ovs-vsctl @@ -24,8 +25,7 @@ EXTRA_DIST += \ utilities/ovs-pki-cgi.in \ utilities/ovs-pki.8.in \ utilities/ovs-pki.in \ - utilities/ovs-vsctl.8.in \ - utilities/ovs-vsctl.in + utilities/ovs-vsctl.8.in DISTCLEANFILES += \ utilities/ovs-appctl.8 \ utilities/ovs-controller.8 \ @@ -38,7 +38,6 @@ DISTCLEANFILES += \ utilities/ovs-pki \ utilities/ovs-pki-cgi \ utilities/ovs-pki.8 \ - utilities/ovs-vsctl \ utilities/ovs-vsctl.8 man_MANS += \ @@ -77,6 +76,9 @@ utilities_ovs_openflowd_LDADD = \ $(FAULT_LIBS) \ $(SSL_LIBS) +utilities_ovs_vsctl_SOURCES = utilities/ovs-vsctl.c vswitchd/vswitch-idl.c +utilities_ovs_vsctl_LDADD = lib/libopenvswitch.a $(FAULT_LIBS) + utilities_ovs_wdt_SOURCES = utilities/ovs-wdt.c utilities_nlmon_SOURCES = utilities/nlmon.c diff --git a/utilities/ovs-vsctl.c b/utilities/ovs-vsctl.c new file mode 100644 index 00000000..bff603a4 --- /dev/null +++ b/utilities/ovs-vsctl.c @@ -0,0 +1,988 @@ +/* + * Copyright (c) 2009 Nicira Networks. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "command-line.h" +#include "compiler.h" +#include "dirs.h" +#include "dynamic-string.h" +#include "ovsdb-idl.h" +#include "poll-loop.h" +#include "vswitchd/vswitch-idl.h" +#include "timeval.h" +#include "util.h" + +#include "vlog.h" +#define THIS_MODULE VLM_vsctl + +/* --db: The database server to contact. */ +static const char *db; + +/* --oneline: Write each command's output as a single line? */ +static bool oneline; + +static char *default_db(void); +static void usage(void) NO_RETURN; +static void parse_options(int argc, char *argv[]); + +static void do_vsctl(int argc, char *argv[], struct ovsdb_idl *idl); + +int +main(int argc, char *argv[]) +{ + struct ovsdb_idl *idl; + unsigned int seqno; + int trials; + + set_program_name(argv[0]); + signal(SIGPIPE, SIG_IGN); + time_init(); + vlog_init(); + parse_options(argc, argv); + + idl = ovsdb_idl_create(db, &ovsrec_idl_class); + seqno = ovsdb_idl_get_seqno(idl); + trials = 0; + for (;;) { + unsigned int new_seqno; + + ovsdb_idl_run(idl); + new_seqno = ovsdb_idl_get_seqno(idl); + if (new_seqno != seqno) { + if (++trials > 5) { + ovs_fatal(0, "too many database inconsistency failures"); + } + do_vsctl(argc - optind, argv + optind, idl); + seqno = new_seqno; + } + + ovsdb_idl_wait(idl); + poll_block(); + } +} + +static void +parse_options(int argc, char *argv[]) +{ + enum { + OPT_DB = UCHAR_MAX + 1, + OPT_ONELINE, + }; + static struct option long_options[] = { + {"db", required_argument, 0, OPT_DB}, + {"oneline", no_argument, 0, OPT_ONELINE}, + {"verbose", optional_argument, 0, 'v'}, + {"help", no_argument, 0, 'h'}, + {"version", no_argument, 0, 'V'}, + {0, 0, 0, 0}, + }; + char *short_options; + + short_options = xasprintf ("+%s", + long_options_to_short_options(long_options)); + for (;;) { + int c; + + c = getopt_long(argc, argv, short_options, long_options, NULL); + if (c == -1) { + break; + } + + switch (c) { + case OPT_DB: + db = optarg; + break; + + case OPT_ONELINE: + oneline = true; + break; + + case 'h': + usage(); + + case 'V': + OVS_PRINT_VERSION(0, 0); + exit(EXIT_SUCCESS); + + case 'v': + vlog_set_verbosity(optarg); + break; + + case '?': + exit(EXIT_FAILURE); + + default: + abort(); + } + } + free(short_options); + + if (!db) { + db = default_db(); + } +} + +static void +usage(void) +{ + printf("%s: ovs-vswitchd management utility\n" + "usage: %s [OPTIONS] COMMAND [ARG...]\n", + program_name, program_name); + printf("\nBridge commands:\n" + " add-br BRIDGE " + "create a new bridge named BRIDGE\n" + " add-br BRIDGE PARENT VLAN " + "create new fake bridge BRIDGE in PARENT on VLAN\n" + " del-br BRIDGE " + "delete BRIDGE and all of its ports\n" + " list-br " + "print the names of all the bridges\n" + " br-exists BRIDGE " + "test whether BRIDGE exists\n" + " br-to-vlan BRIDGE " + "print the VLAN which BRIDGE is on\n" + " br-to-parent BRIDGE " + "print the parent of BRIDGE\n"); + printf("\nPort commands:\n" + " list-ports BRIDGE " + "print the names of all the ports on BRIDGE\n" + " add-port BRIDGE PORT " + "add network device PORT to BRIDGE\n" + " add-bond BRIDGE PORT IFACE... " + "add new bonded port PORT in BRIDGE from IFACES\n" + " del-port [BRIDGE] PORT " + "delete PORT (which may be bonded) from BRIDGE\n" + " port-to-br PORT " + "print name of bridge that contains PORT\n" + "A bond is considered to be a single port.\n"); + printf("\nInterface commands (a bond consists of multiple interfaces):\n" + " list-ifaces BRIDGE " + "print the names of all the interfaces on BRIDGE\n" + " iface-to-br IFACE " + "print name of bridge that contains IFACE\n"); + printf("\nOptions:\n" + " --db=DATABASE " + "connect to DATABASE\n" + " " + "(default: %s)\n" + " --oneline " + "print exactly one line of output per command\n", + default_db()); + vlog_usage(); + printf("\nOther options:\n" + " -h, --help " + "display this help message\n" + " -V, --version " + "display version information\n"); + exit(EXIT_SUCCESS); +} + +static char * +default_db(void) +{ + static char *def; + if (!def) { + def = xasprintf("%s/ovsdb-server", ovs_rundir); + } + return def; +} + +struct vsctl_bridge { + struct ovsrec_bridge *br_cfg; + char *name; + struct vsctl_bridge *parent; + int vlan; +}; + +struct vsctl_port { + struct ovsrec_port *port_cfg; + struct vsctl_bridge *bridge; +}; + +struct vsctl_iface { + struct ovsrec_interface *iface_cfg; + struct vsctl_port *port; +}; + +struct vsctl_info { + struct shash bridges; + struct shash ports; + struct shash ifaces; +}; + +static struct ovsdb_idl_txn * +txn_from_openvswitch(const struct ovsrec_open_vswitch *ovs) +{ + return ovsdb_idl_txn_get(&ovs->header_); +} + +static struct vsctl_bridge * +add_bridge(struct vsctl_info *b, + struct ovsrec_bridge *br_cfg, const char *name, + struct vsctl_bridge *parent, int vlan) +{ + struct vsctl_bridge *br = xmalloc(sizeof *br); + br->br_cfg = br_cfg; + br->name = xstrdup(name); + br->parent = parent; + br->vlan = vlan; + shash_add(&b->bridges, br->name, br); + return br; +} + +static bool +port_is_fake_bridge(const struct ovsrec_port *port_cfg) +{ + return (port_cfg->fake_bridge + && port_cfg->tag + && *port_cfg->tag >= 1 && *port_cfg->tag <= 4095); +} + +static struct vsctl_bridge * +find_vlan_bridge(struct vsctl_info *info, + struct vsctl_bridge *parent, int vlan) +{ + struct shash_node *node; + + SHASH_FOR_EACH (node, &info->bridges) { + struct vsctl_bridge *br = node->data; + if (br->parent == parent && br->vlan == vlan) { + return br; + } + } + + return NULL; +} + +static void +free_info(struct vsctl_info *info) +{ + struct shash_node *node; + + SHASH_FOR_EACH (node, &info->bridges) { + struct vsctl_bridge *bridge = node->data; + free(bridge->name); + free(bridge); + } + shash_destroy(&info->bridges); + + SHASH_FOR_EACH (node, &info->ports) { + struct vsctl_port *port = node->data; + free(port); + } + shash_destroy(&info->ports); + + SHASH_FOR_EACH (node, &info->ifaces) { + struct vsctl_iface *iface = node->data; + free(iface); + } + shash_destroy(&info->ifaces); +} + +static void +get_info(const struct ovsrec_open_vswitch *ovs, struct vsctl_info *info) +{ + struct shash bridges, ports; + size_t i; + + shash_init(&info->bridges); + shash_init(&info->ports); + shash_init(&info->ifaces); + + shash_init(&bridges); + shash_init(&ports); + for (i = 0; i < ovs->n_bridges; i++) { + struct ovsrec_bridge *br_cfg = ovs->bridges[i]; + struct vsctl_bridge *br; + size_t j; + + if (!shash_add_once(&bridges, br_cfg->name, NULL)) { + VLOG_WARN("%s: database contains duplicate bridge name", + br_cfg->name); + continue; + } + br = add_bridge(info, br_cfg, br_cfg->name, NULL, 0); + if (!br) { + continue; + } + + for (j = 0; j < br_cfg->n_ports; j++) { + struct ovsrec_port *port_cfg = br_cfg->ports[j]; + + if (!shash_add_once(&ports, port_cfg->name, NULL)) { + VLOG_WARN("%s: database contains duplicate port name", + port_cfg->name); + continue; + } + + if (port_is_fake_bridge(port_cfg) + && shash_add_once(&bridges, br_cfg->name, NULL)) { + add_bridge(info, NULL, port_cfg->name, br, *port_cfg->tag); + } + } + } + shash_destroy(&bridges); + shash_destroy(&ports); + + shash_init(&bridges); + shash_init(&ports); + for (i = 0; i < ovs->n_bridges; i++) { + struct ovsrec_bridge *br_cfg = ovs->bridges[i]; + struct vsctl_bridge *br; + size_t j; + + if (!shash_add_once(&bridges, br_cfg->name, NULL)) { + continue; + } + br = shash_find_data(&info->bridges, br_cfg->name); + for (j = 0; j < br_cfg->n_ports; j++) { + struct ovsrec_port *port_cfg = br_cfg->ports[j]; + struct vsctl_port *port; + size_t k; + + if (!shash_add_once(&ports, port_cfg->name, NULL)) { + continue; + } + + if (port_is_fake_bridge(port_cfg) + && !shash_add_once(&bridges, br_cfg->name, NULL)) { + continue; + } + + port = xmalloc(sizeof *port); + port->port_cfg = port_cfg; + if (port_cfg->tag + && *port_cfg->tag >= 1 && *port_cfg->tag <= 4095) { + port->bridge = find_vlan_bridge(info, br, *port_cfg->tag); + if (!port->bridge) { + port->bridge = br; + } + } else { + port->bridge = br; + } + shash_add(&info->ports, port_cfg->name, port); + + for (k = 0; k < port_cfg->n_interfaces; k++) { + struct ovsrec_interface *iface_cfg = port_cfg->interfaces[k]; + struct vsctl_iface *iface; + + if (shash_find(&info->ifaces, iface_cfg->name)) { + VLOG_WARN("%s: database contains duplicate interface name", + iface_cfg->name); + continue; + } + + iface = xmalloc(sizeof *iface); + iface->iface_cfg = iface_cfg; + iface->port = port; + } + } + } + shash_destroy(&bridges); + shash_destroy(&ports); +} + +static void +check_conflicts(struct vsctl_info *info, const char *name, + char *msg) +{ + struct vsctl_iface *iface; + struct vsctl_port *port; + + if (shash_find(&info->bridges, name)) { + ovs_fatal(0, "%s because a bridge named %s already exists", msg, name); + } + + port = shash_find_data(&info->ports, name); + if (port) { + ovs_fatal(0, "%s because a port named %s already exists on bridge %s", + msg, name, port->bridge->name); + } + + iface = shash_find_data(&info->ifaces, name); + if (iface) { + ovs_fatal(0, "%s because an interface named %s already exists " + "on bridge %s", msg, name, iface->port->bridge->name); + } + + free(msg); +} + +static struct vsctl_bridge * +find_bridge(struct vsctl_info *info, const char *name) +{ + struct vsctl_bridge *br = shash_find_data(&info->bridges, name); + if (!br) { + ovs_fatal(0, "no bridge named %s", name); + } + return br; +} + +static struct vsctl_port * +find_port(struct vsctl_info *info, const char *name) +{ + struct vsctl_port *port = shash_find_data(&info->ports, name); + if (!port || !strcmp(name, port->bridge->name)) { + ovs_fatal(0, "no port named %s", name); + } + return port; +} + +static struct vsctl_iface * +find_iface(struct vsctl_info *info, const char *name) +{ + struct vsctl_iface *iface = shash_find_data(&info->ifaces, name); + if (!iface) { + ovs_fatal(0, "no interface named %s", name); + } + return iface; +} + +static void +bridge_insert_port(struct ovsrec_bridge *br, struct ovsrec_port *port) +{ + struct ovsrec_port **ports; + size_t i; + + ports = xmalloc(sizeof *br->ports * (br->n_ports + 1)); + for (i = 0; i < br->n_ports; i++) { + ports[i] = br->ports[i]; + } + printf("bridge has %zu ports, adding 1\n", br->n_ports); + ports[br->n_ports] = port; + ovsrec_bridge_set_ports(br, ports, br->n_ports + 1); + free(ports); +} + +static void +bridge_delete_port(struct ovsrec_bridge *br, struct ovsrec_port *port) +{ + struct ovsrec_port **ports; + size_t i, n; + + ports = xmalloc(sizeof *br->ports * br->n_ports); + for (i = n = 0; i < br->n_ports; i++) { + if (br->ports[i] != port) { + ports[n++] = br->ports[i]; + } + } + ovsrec_bridge_set_ports(br, ports, n); + free(ports); +} + +static void +ovs_insert_bridge(const struct ovsrec_open_vswitch *ovs, + struct ovsrec_bridge *bridge) +{ + struct ovsrec_bridge **bridges; + size_t i; + + bridges = xmalloc(sizeof *ovs->bridges * (ovs->n_bridges + 1)); + for (i = 0; i < ovs->n_bridges; i++) { + bridges[i] = ovs->bridges[i]; + } + bridges[ovs->n_bridges] = bridge; + ovsrec_open_vswitch_set_bridges(ovs, bridges, ovs->n_bridges + 1); + free(bridges); +} + +static void +ovs_delete_bridge(const struct ovsrec_open_vswitch *ovs, + struct ovsrec_bridge *bridge) +{ + struct ovsrec_bridge **bridges; + size_t i, n; + + bridges = xmalloc(sizeof *ovs->bridges * ovs->n_bridges); + for (i = n = 0; i < ovs->n_bridges; i++) { + if (ovs->bridges[i] != bridge) { + bridges[n++] = ovs->bridges[i]; + } + } + ovsrec_open_vswitch_set_bridges(ovs, bridges, n); + free(bridges); +} + +static void +cmd_add_br(int argc, char *argv[], const struct ovsrec_open_vswitch *ovs, + struct ds *output UNUSED) +{ + const char *br_name = argv[1]; + struct vsctl_info info; + + get_info(ovs, &info); + check_conflicts(&info, br_name, + xasprintf("cannot create a bridge named %s", br_name)); + + if (argc == 2) { + struct ovsrec_bridge *br; + struct ovsrec_port *port; + struct ovsrec_interface *iface; + + iface = ovsrec_interface_insert(txn_from_openvswitch(ovs)); + ovsrec_interface_set_name(iface, br_name); + + port = ovsrec_port_insert(txn_from_openvswitch(ovs)); + ovsrec_port_set_name(port, br_name); + ovsrec_port_set_interfaces(port, &iface, 1); + + br = ovsrec_bridge_insert(txn_from_openvswitch(ovs)); + ovsrec_bridge_set_name(br, br_name); + ovsrec_bridge_set_ports(br, &port, 1); + + ovs_insert_bridge(ovs, br); + } else if (argc == 3) { + ovs_fatal(0, "'%s' comamnd takes exactly 1 or 3 arguments", argv[0]); + } else if (argc >= 4) { + const char *parent_name = argv[2]; + int vlan = atoi(argv[3]); + struct ovsrec_bridge *br; + struct vsctl_bridge *parent; + struct ovsrec_port *port; + struct ovsrec_interface *iface; + int64_t tag = vlan; + + if (vlan < 1 || vlan > 4095) { + ovs_fatal(0, "%s: vlan must be between 1 and 4095", argv[0]); + } + + parent = shash_find_data(&info.bridges, parent_name); + if (parent && parent->vlan) { + ovs_fatal(0, "cannot create brdige with fake bridge as parent"); + } + if (!parent) { + ovs_fatal(0, "parent bridge %s does not exist", parent_name); + } + br = parent->br_cfg; + + iface = ovsrec_interface_insert(txn_from_openvswitch(ovs)); + ovsrec_interface_set_name(iface, br_name); + ovsrec_interface_set_type(iface, "internal"); + + port = ovsrec_port_insert(txn_from_openvswitch(ovs)); + ovsrec_port_set_name(port, br_name); + ovsrec_port_set_interfaces(port, &iface, 1); + ovsrec_port_set_fake_bridge(port, true); + ovsrec_port_set_tag(port, &tag, 1); + } else { + NOT_REACHED(); + } + + free_info(&info); +} + +static void +del_port(struct vsctl_info *info, struct vsctl_port *port) +{ + struct shash_node *node; + + SHASH_FOR_EACH (node, &info->ifaces) { + struct vsctl_iface *iface = node->data; + if (iface->port == port) { + ovsrec_interface_delete(iface->iface_cfg); + } + } + ovsrec_port_delete(port->port_cfg); + + bridge_delete_port((port->bridge->parent + ? port->bridge->parent->br_cfg + : port->bridge->br_cfg), port->port_cfg); +} + +static void +cmd_del_br(int argc UNUSED, char *argv[], + const struct ovsrec_open_vswitch *ovs, struct ds *output UNUSED) +{ + struct shash_node *node; + struct vsctl_info info; + struct vsctl_bridge *bridge; + + get_info(ovs, &info); + bridge = find_bridge(&info, argv[1]); + SHASH_FOR_EACH (node, &info.ports) { + struct vsctl_port *port = node->data; + if (port->bridge == bridge) { + del_port(&info, port); + } + } + if (bridge->br_cfg) { + ovsrec_bridge_delete(bridge->br_cfg); + ovs_delete_bridge(ovs, bridge->br_cfg); + } + free_info(&info); +} + +static void +cmd_list_br(int argc UNUSED, char *argv[] UNUSED, + const struct ovsrec_open_vswitch *ovs, struct ds *output) +{ + struct shash_node *node; + struct vsctl_info info; + + get_info(ovs, &info); + SHASH_FOR_EACH (node, &info.bridges) { + struct vsctl_bridge *br = node->data; + ds_put_format(output, "%s\n", br->name); + } + free_info(&info); +} + +static void +cmd_br_exists(int argc UNUSED, char *argv[], + const struct ovsrec_open_vswitch *ovs, struct ds *output UNUSED) +{ + struct vsctl_info info; + + get_info(ovs, &info); + if (!shash_find_data(&info.bridges, argv[1])) { + exit(2); + } + free_info(&info); +} + +static void +cmd_list_ports(int argc UNUSED, char *argv[], + const struct ovsrec_open_vswitch *ovs, struct ds *output) +{ + struct vsctl_bridge *br; + struct shash_node *node; + struct vsctl_info info; + + get_info(ovs, &info); + br = find_bridge(&info, argv[1]); + SHASH_FOR_EACH (node, &info.ports) { + struct vsctl_port *port = node->data; + + if (strcmp(port->port_cfg->name, br->name) && br == port->bridge) { + ds_put_format(output, "%s\n", port->port_cfg->name); + } + } + free_info(&info); +} + +static void +add_port(const struct ovsrec_open_vswitch *ovs, + const char *br_name, const char *port_name, + char *iface_names[], int n_ifaces) +{ + struct vsctl_info info; + struct vsctl_bridge *bridge; + struct ovsrec_interface **ifaces; + struct ovsrec_port *port; + size_t i; + + get_info(ovs, &info); + check_conflicts(&info, port_name, + xasprintf("cannot create a port named %s", port_name)); + /* XXX need to check for conflicts on interfaces too */ + bridge = find_bridge(&info, br_name); + + ifaces = xmalloc(n_ifaces * sizeof *ifaces); + for (i = 0; i < n_ifaces; i++) { + ifaces[i] = ovsrec_interface_insert(txn_from_openvswitch(ovs)); + ovsrec_interface_set_name(ifaces[i], iface_names[i]); + } + + port = ovsrec_port_insert(txn_from_openvswitch(ovs)); + ovsrec_port_set_name(port, port_name); + ovsrec_port_set_interfaces(port, ifaces, n_ifaces); + if (bridge->vlan) { + int64_t tag = bridge->vlan; + ovsrec_port_set_tag(port, &tag, 1); + } + + bridge_insert_port((bridge->parent ? bridge->parent->br_cfg + : bridge->br_cfg), port); + + free_info(&info); +} + +static void +cmd_add_port(int argc UNUSED, char *argv[], + const struct ovsrec_open_vswitch *ovs, struct ds *output UNUSED) +{ + add_port(ovs, argv[1], argv[2], &argv[2], 1); +} + +static void +cmd_add_bond(int argc, char *argv[], + const struct ovsrec_open_vswitch *ovs, struct ds *output UNUSED) +{ + add_port(ovs, argv[1], argv[2], &argv[3], argc - 3); +} + +static void +cmd_del_port(int argc, char *argv[], + const struct ovsrec_open_vswitch *ovs, struct ds *output UNUSED) +{ + struct vsctl_info info; + + get_info(ovs, &info); + if (argc == 2) { + struct vsctl_port *port = find_port(&info, argv[1]); + del_port(&info, port); + } else if (argc == 3) { + struct vsctl_bridge *bridge = find_bridge(&info, argv[1]); + struct vsctl_port *port = find_port(&info, argv[2]); + + if (port->bridge == bridge) { + del_port(&info, port); + } else if (port->bridge->parent == bridge) { + ovs_fatal(0, "bridge %s does not have a port %s (although its " + "parent bridge %s does)", + argv[1], argv[2], bridge->parent->name); + } else { + ovs_fatal(0, "bridge %s does not have a port %s", + argv[1], argv[2]); + } + } + free_info(&info); +} + +static void +cmd_port_to_br(int argc UNUSED, char *argv[], + const struct ovsrec_open_vswitch *ovs, struct ds *output) +{ + struct vsctl_port *port; + struct vsctl_info info; + + get_info(ovs, &info); + port = find_port(&info, argv[1]); + ds_put_format(output, "%s\n", port->bridge->name); + free_info(&info); +} + +static void +cmd_br_to_vlan(int argc UNUSED, char *argv[], + const struct ovsrec_open_vswitch *ovs, struct ds *output) +{ + struct vsctl_bridge *bridge; + struct vsctl_info info; + + get_info(ovs, &info); + bridge = find_bridge(&info, argv[1]); + ds_put_format(output, "%d\n", bridge->vlan); + free_info(&info); +} + +static void +cmd_br_to_parent(int argc UNUSED, char *argv[], + const struct ovsrec_open_vswitch *ovs, struct ds *output) +{ + struct vsctl_bridge *bridge; + struct vsctl_info info; + + get_info(ovs, &info); + bridge = find_bridge(&info, argv[1]); + if (bridge->parent) { + bridge = bridge->parent; + } + ds_put_format(output, "%s\n", bridge->name); + free_info(&info); +} + +static void +cmd_list_ifaces(int argc UNUSED, char *argv[], + const struct ovsrec_open_vswitch *ovs, struct ds *output) +{ + struct vsctl_bridge *br; + struct shash_node *node; + struct vsctl_info info; + + get_info(ovs, &info); + br = find_bridge(&info, argv[1]); + SHASH_FOR_EACH (node, &info.ifaces) { + struct vsctl_iface *iface = node->data; + + if (br == iface->port->bridge) { + ds_put_format(output, "%s\n", iface->iface_cfg->name); + } + } + free_info(&info); +} + +static void +cmd_iface_to_br(int argc UNUSED, char *argv[], + const struct ovsrec_open_vswitch *ovs, struct ds *output) +{ + struct vsctl_iface *iface; + struct vsctl_info info; + + get_info(ovs, &info); + iface = find_iface(&info, argv[1]); + ds_put_format(output, "%s\n", iface->port->bridge->name); + free_info(&info); +} + +struct vsctl_command { + const char *name; + int min_args; + int max_args; + void (*handler)(int argc, char *argv[], + const struct ovsrec_open_vswitch *ovs, struct ds *output); +}; + +static void run_vsctl_command(int argc, char *argv[], + const struct ovsrec_open_vswitch *ovs, + struct ds *output); + +static void +do_vsctl(int argc, char *argv[], struct ovsdb_idl *idl) +{ + struct ovsdb_idl_txn *txn; + const struct ovsrec_open_vswitch *ovs; + enum ovsdb_idl_txn_status status; + struct ds *output; + int n_output; + int i, start; + + ovs = ovsrec_open_vswitch_first(idl); + if (!ovs) { + /* XXX it would be more user-friendly to create a record ourselves + * (while verifying that the table is empty before doing so). */ + ovs_fatal(0, "%s: database does not contain any Open vSwitch " + "configuration", db); + } + + txn = ovsdb_idl_txn_create(idl); + output = xmalloc(argc * sizeof *output); + n_output = 0; + for (start = i = 0; i < argc; i++) { + if (!strcmp(argv[i], "--")) { + if (i > start) { + ds_init(&output[n_output]); + run_vsctl_command(i - start, &argv[start], ovs, + &output[n_output++]); + } + start = i + 1; + } + } + if (i > start) { + ds_init(&output[n_output]); + run_vsctl_command(i - start, &argv[start], ovs, &output[n_output++]); + } + + while ((status = ovsdb_idl_txn_commit(txn)) == TXN_INCOMPLETE) { + ovsdb_idl_run(idl); + ovsdb_idl_wait(idl); + poll_block(); + } + ovsdb_idl_txn_destroy(txn); + + switch (status) { + case TXN_INCOMPLETE: + NOT_REACHED(); + + case TXN_ABORTED: + /* Should not happen--we never call ovsdb_idl_txn_abort(). */ + ovs_fatal(0, "transaction aborted"); + + case TXN_SUCCESS: + break; + + case TXN_TRY_AGAIN: + for (i = 0; i < n_output; i++) { + ds_destroy(&output[i]); + } + return; + + case TXN_ERROR: + ovs_fatal(0, "transaction error"); + + default: + NOT_REACHED(); + } + + for (i = 0; i < n_output; i++) { + struct ds *ds = &output[i]; + if (oneline) { + size_t j; + + ds_chomp(ds, '\n'); + for (j = 0; j < ds->length; j++) { + int c = ds->string[j]; + switch (c) { + case '\n': + fputs("\\n", stdout); + break; + + case '\\': + fputs("\\\\", stdout); + break; + + default: + putchar(c); + } + } + putchar('\n'); + } else { + fputs(ds_cstr(ds), stdout); + } + } + exit(EXIT_SUCCESS); +} + +static void +run_vsctl_command(int argc, char *argv[], + const struct ovsrec_open_vswitch *ovs, struct ds *output) +{ + static const struct vsctl_command all_commands[] = { + {"add-br", 1, 3, cmd_add_br}, + {"del-br", 1, 1, cmd_del_br}, + {"list-br", 0, 0, cmd_list_br}, + {"br-exists", 1, 1, cmd_br_exists}, + {"list-ports", 1, 1, cmd_list_ports}, + {"add-port", 2, 2, cmd_add_port}, + {"add-bond", 4, INT_MAX, cmd_add_bond}, + {"del-port", 1, 2, cmd_del_port}, + {"port-to-br", 1, 1, cmd_port_to_br}, + {"br-to-vlan", 1, 1, cmd_br_to_vlan}, + {"br-to-parent", 1, 1, cmd_br_to_parent}, + {"list-ifaces", 1, 1, cmd_list_ifaces}, + {"iface-to-br", 1, 1, cmd_iface_to_br}, + }; + + const struct vsctl_command *p; + + assert(argc > 0); + for (p = all_commands; p->name != NULL; p++) { + if (!strcmp(p->name, argv[0])) { + int n_arg = argc - 1; + if (n_arg < p->min_args) { + ovs_fatal(0, "'%s' command requires at least %d arguments", + p->name, p->min_args); + } else if (n_arg > p->max_args) { + ovs_fatal(0, "'%s' command takes at most %d arguments", + p->name, p->max_args); + } else { + p->handler(argc, argv, ovs, output); + return; + } + } + } + + ovs_fatal(0, "unknown command '%s'; use --help for help", argv[0]); +} diff --git a/utilities/ovs-vsctl.in b/utilities/ovs-vsctl.in deleted file mode 100755 index bef868cf..00000000 --- a/utilities/ovs-vsctl.in +++ /dev/null @@ -1,609 +0,0 @@ -#! @PYTHON@ -# Copyright (c) 2009 Nicira Networks. -*- python -*- -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at: -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import errno -import fcntl -import fnmatch -import getopt -import os -import re -import socket -import stat -import sys -import syslog - -argv0 = sys.argv[0] -if argv0.find('/') >= 0: - argv0 = argv0[argv0.rfind('/') + 1:] - -DEFAULT_VSWITCHD_CONF = "@sysconfdir@/ovs-vswitchd.conf" -vswitchd_conf = DEFAULT_VSWITCHD_CONF - -DEFAULT_VSWITCHD_TARGET = "ovs-vswitchd" -vswitchd_target = DEFAULT_VSWITCHD_TARGET - -reload_vswitchd = True - -enable_syslog = True - -class Error(Exception): - def __init__(self, msg): - Exception.__init__(self) - self.msg = msg - -def log(message): - if enable_syslog: - syslog.syslog(message) - -# XXX Most of the functions below should be integrated into a -# VSwitchConfiguration object with logically named fields and methods -# instead of this mishmash of functionality. - -# Locks 'filename' for writing. -def cfg_lock(filename): - if filename == '-': - return - - if '/' in filename: - lastSlash = filename.rfind('/') - prefix = filename[:lastSlash] - suffix = filename[lastSlash + 1:] - lock_name = "%s/.%s.~lock~" % (prefix, suffix) - else: - lock_name = ".%s.~lock~" % filename - - while True: - # Try to open an existing lock file. - try: - f = open(lock_name, 'r') - except IOError, e: - if e.errno != errno.ENOENT: - raise - - # Try to create a new lock file. - try: - fd = os.open(lock_name, os.O_RDWR | os.O_CREAT | os.O_EXCL, 0600) - except OSError, e: - if e.errno != errno.EEXIST: - raise - # Someone else created the lock file, try again. - os.close(fd) - continue - - fcntl.flock(f, fcntl.LOCK_EX) - return - -# Read the ovs-vswitchd.conf file named 'filename' and return its contents as a -# dictionary that maps from string keys to lists of string values. (Even -# singleton values are represented as lists.) -def cfg_read(filename, lock=False): - if lock: - cfg_lock(filename) - - try: - if filename == '-': - f = open('/dev/stdin') - else: - f = open(filename) - except IOError, e: - sys.stderr.write("%s: could not open %s (%s)\n" - % (argv0, filename, e.strerror)) - sys.exit(1) - - cfg = {} - rx = re.compile('([-._@$:+a-zA-Z0-9]+)(?:[ \t\r\n\v]*)=(?:[ \t\r\n\v]*)(.*)$') - for line in f: - line = line.strip() - if len(line) == 0 or line[0] == '#': - continue - - match = rx.match(line) - if match == None: - continue - - key, value = match.groups() - if key not in cfg: - cfg[key] = [] - cfg[key].append(value) - - global orig_cfg - orig_cfg = cfg_clone(cfg) - - return cfg - -# Returns a deep copy of 'cfg', which must be in the format returned -# by cfg_read(). -def cfg_clone(cfg): - new = {} - for key in cfg: - new[key] = list(cfg[key]) - return new - -# Returns a list of all the configuration lines that are in 'a' but -# not in 'b'. -def cfg_subtract(a, b): - difference = [] - for key in a: - for value in a[key]: - if key not in b or value not in b[key]: - difference.append("%s=%s" % (key, value)) - return difference - -def do_cfg_save(cfg, file): - # Log changes. - added = cfg_subtract(cfg, orig_cfg) - removed = cfg_subtract(orig_cfg, cfg) - if added or removed: - log("configuration changes:") - for line in removed: - log("-%s\n" % line) - for line in added: - log("+%s\n" % line) - - # Write changes. - for key in sorted(cfg.keys()): - for value in sorted(cfg[key]): - file.write("%s=%s\n" % (key, value)) - -def cfg_reload(): - target = VSWITCHD_TARGET - if not target.startswith('/'): - pid = read_first_line_of_file('%s/%s.pid' % ('@RUNDIR@', target)) - target = '%s/%s.%s.ctl' % ('@RUNDIR@', target, pid) - s = os.stat(target) - if not stat.S_ISSOCK(s.st_mode): - raise Error("%s is not a Unix domain socket, cannot reload" % target) - skt = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - skt.connect(target) - f = os.fdopen(skt.fileno(), "r+") - f.write("vswitchd/reload\n") - f.flush() - f.readline() - f.close() - -def cfg_save(cfg, filename): - if filename == '-': - do_cfg_save(cfg, sys.stdout) - else: - tmp_name = filename + ".~tmp~" - f = open(tmp_name, 'w') - do_cfg_save(cfg, f) - f.close() - os.rename(tmp_name, filename) - if reload_vswitchd: - cfg_reload() - -# Returns a set of the immediate subsections of 'section' within 'cfg'. For -# example, if 'section' is "bridge" and keys bridge.a, bridge.b, bridge.b.c, -# and bridge.c.x.y.z exist, returns set(['a', 'b', 'c']). -def cfg_get_subsections(cfg, section): - subsections = set() - for key in cfg: - if key.startswith(section + "."): - dot = key.find(".", len(section) + 1) - if dot == -1: - dot = len(key) - subsections.add(key[len(section) + 1:dot]) - return subsections - -# Returns True if 'cfg' contains a key whose single value is 'true'. Otherwise -# returns False. -def cfg_get_bool(cfg, name): - return name in cfg and cfg[name] == ['true'] - -# If 'cfg' has a port named 'port' configured with an implicit VLAN, returns -# that VLAN number. Otherwise, returns 0. -def get_port_vlan(cfg, port): - try: - return int(cfg["vlan.%s.tag" % port][0]) - except (ValueError, KeyError): - return 0 - -# Returns all the ports within 'bridge' in 'cfg'. If 'vlan' is nonnegative, -# the ports returned are only those configured with implicit VLAN 'vlan'. -def get_bridge_ports(cfg, bridge, vlan): - ports = [] - for port in cfg["bridge.%s.port" % bridge]: - if vlan < 0 or get_port_vlan(cfg, port) == vlan: - ports.append(port) - return ports - -# Returns all the interfaces within 'bridge' in 'cfg'. If 'vlan' is -# nonnegative, the interfaces returned are only those whose ports are -# configured with implicit VLAN 'vlan'. -def get_bridge_ifaces(cfg, bridge, vlan): - ifaces = [] - for port in get_bridge_ports(cfg, bridge, vlan): - ifaces.extend(cfg.get("bonding.%s.slave" % port, [port])) - return ifaces - -# Returns the first line of the file named 'name', with the trailing new-line -# (if any) stripped off. -def read_first_line_of_file(name): - file = None - try: - file = open(name, 'r') - return file.readline().rstrip('\n') - finally: - if file != None: - file.close() - -# Returns a bridge ID constructed from the MAC address of network device -# 'netdev', in the format "8000.000102030405". -def get_bridge_id(netdev): - try: - hwaddr = read_first_line_of_file("/sys/class/net/%s/address" % netdev) - return "8000.%s" % (hwaddr.replace(":", "")) - except: - return "8000.002320ffffff" - -# Returns a list of 3-tuples based on 'cfg'. Each 3-tuple represents -# one real bridge or one fake bridge and has the form (bridge, parent, -# vlan), where 'bridge' is the real or fake bridge name, 'parent' is -# the same as 'bridge' for a real bridge or the name of the containing -# bridge for a fake bridge, and 'vlan' is 0 for a real bridge or a -# VLAN number for a fake bridge. -def get_bridge_info(cfg): - real_bridges = [(br, br, 0) for br in get_real_bridges(cfg)] - fake_bridges = [] - for linux_bridge, ovs_bridge, vlan in real_bridges: - for iface in get_bridge_ifaces(cfg, ovs_bridge, -1): - if cfg_get_bool(cfg, "iface.%s.fake-bridge" % iface): - fake_bridges.append((iface, ovs_bridge, - get_port_vlan(cfg, iface))) - return real_bridges + fake_bridges - -# Returns the real bridges configured in 'cfg'. -def get_real_bridges(cfg): - return cfg_get_subsections(cfg, "bridge") - -# Returns the fake bridges configured in 'cfg'. -def get_fake_bridges(cfg): - return [bridge for bridge, parent, vlan in get_bridge_info(cfg) - if bridge != parent] - -# Returns all the real and fake bridges configured in 'cfg'. -def get_all_bridges(cfg): - return [bridge for bridge, parent, vlan in get_bridge_info(cfg)] - -# Returns the parent bridge and VLAN of real or fake 'bridge' in -# 'cfg', where the parent bridge and VLAN are as defined in the -# description of get_bridge_info(). Raises an error if no bridge -# named 'bridge' exists in 'cfg'. -def find_bridge(cfg, bridge): - for br, parent, vlan in get_bridge_info(cfg): - if br == bridge: - return parent, vlan - raise Error("no bridge named %s" % bridge) - -def del_matching_keys(cfg, pattern): - for key in [key for key in cfg.keys() if fnmatch.fnmatch(key, pattern)]: - del cfg[key] - -# Deletes anything related to a port named 'port' from 'cfg'. No port -# named 'port' need actually exist; this function will clean up -# regardless. -def del_port(cfg, port): - # The use of [!0-9] keeps an interface of 'eth0' from matching - # VLANs attached to eth0 (such as 'eth0.123'), which are distinct - # interfaces. - for iface in cfg.get('bonding.%s.slave' % port, [port]): - del_matching_keys(cfg, 'iface.%s.[!0-9]*' % iface) - # Yes, this "port" setting applies to interfaces, not ports, *sigh*. - del_matching_keys(cfg, 'port.%s.ingress-policing*' % iface) - del_matching_keys(cfg, 'bonding.%s.[!0-9]*' % port) - del_matching_keys(cfg, 'vlan.%s.[!0-9]*' % port) - for key in cfg.keys(): - if fnmatch.fnmatch(key, 'bridge.*.port'): - cfg[key] = [s for s in cfg[key] if s != port] - -# Returns the name of the (real or fake) bridge in 'cfg' that contains -# port 'port', or None if there is no such port. -def port_to_bridge(cfg, port): - for bridge, parent, vlan in get_bridge_info(cfg): - if port != bridge and port in get_bridge_ports(cfg, parent, vlan): - return bridge - return None - -def usage(): - print """%(argv0)s: ovs-vswitchd management utility -usage: %(argv0)s [OPTIONS] COMMAND [ARG...] - -Bridge commands: - add-br BRIDGE create a new bridge named BRIDGE - add-br BRIDGE PARENT VLAN create new fake bridge BRIDGE in PARENT on VLAN - del-br BRIDGE delete BRIDGE and all of its ports - list-br print the names of all the bridges - br-exists BRIDGE test whether BRIDGE exists - br-to-vlan BRIDGE print the VLAN which BRIDGE is on - br-to-parent BRIDGE print the parent of BRIDGE - -Port commands: - list-ports BRIDGE print the names of all the ports on BRIDGE - add-port BRIDGE PORT add network device PORT to BRIDGE - add-bond BRIDGE PORT IFACE... add new bonded port PORT in BRIDGE from IFACES - del-port [BRIDGE] PORT delete PORT (which may be bonded) from BRIDGE - port-to-br PORT print name of bridge that contains PORT -A bond is considered to be a single port. - -Interface commands (a bond consists of multiple interfaces): - list-ifaces BRIDGE print the names of all the interfaces on BRIDGE - iface-to-br IFACE print name of bridge that contains IFACE -A bond is considered to consist of interfaces. - -General options: - --no-syslog do not write mesages to syslog - -c, --config=FILE set configuration file - (default: %(config)s) - -t, --target=PROGRAM|SOCKET set ovs-vswitchd target - (default: %(target)s) - --no-reload do not make ovs-vswitchd reload its configuration - -h, --help display this help message and exit - -V, --version display version information and exit -Report bugs to bugs@openvswitch.org.""" % {'argv0': argv0, - 'config': DEFAULT_VSWITCHD_CONF, - 'target': DEFAULT_VSWITCHD_TARGET} - sys.exit(0) - -def version(): - print "ovs-vsctl (Open vSwitch) @VERSION@" - sys.exit(0) - -def check_conflicts(cfg, name, op): - bridges = get_bridge_info(cfg) - if name in [bridge for bridge, parent, vlan in bridges]: - raise Error("%s because a bridge named %s already exists" % (op, name)) - - for bridge, parent, vlan in bridges: - if name in get_bridge_ports(cfg, parent, vlan): - raise Error("%s because a port named %s already exists on bridge %s" % (op, name, bridge)) - if name in get_bridge_ifaces(cfg, parent, vlan): - raise Error("%s because an interface named %s already exists on bridge %s" % (op, name, bridge)) - -def cmd_add_br(cfg, bridge, parent=None, vlan=None): - check_conflicts(cfg, bridge, "cannot create a bridge named %s" % bridge) - - if parent and vlan: - if parent in get_fake_bridges(cfg): - raise Error("cannot create bridge with fake bridge as parent") - if parent not in get_real_bridges(cfg): - raise Error("parent bridge %s does not exist" % bridge) - try: - if int(vlan) < 0 or int(vlan) > 4095: - raise ValueError - except ValueError: - raise Error("invalid VLAN number %s" % vlan) - - # Create fake bridge internal port. - cfg['iface.%s.internal' % bridge] = ['true'] - cfg['iface.%s.fake-bridge' % bridge] = ['true'] - cfg['vlan.%s.tag' % bridge] = [vlan] - - # Add fake bridge port to parent. - cfg['bridge.%s.port' % parent].append(bridge) - else: - cfg['bridge.%s.port' % bridge] = [bridge] - -def cmd_del_br(cfg, bridge): - parent, vlan = find_bridge(cfg, bridge) - if vlan == 0: - vlan = -1 - for port in set(get_bridge_ports(cfg, parent, vlan) + [bridge]): - del_port(cfg, port) - if vlan < 0: - del_matching_keys(cfg, 'bridge.%s.[!0-9]*' % bridge) - -def cmd_list_br(cfg): - return get_all_bridges(cfg) - -def cmd_br_exists(cfg, bridge): - if bridge not in get_all_bridges(cfg): - sys.exit(2) - -def cmd_list_ports(cfg, bridge): - ports = [] - parent, vlan = find_bridge(cfg, bridge) - for port in get_bridge_ports(cfg, parent, vlan): - if port != bridge: - ports.append(port) - return ports - -def do_add_port(cfg, bridge, parent, port, vlan): - check_conflicts(cfg, port, "cannot create a port named %s" % port) - cfg['bridge.%s.port' % parent].append(port) - if vlan > 0: - cfg['vlan.%s.tag' % port] = [vlan] - -def cmd_add_port(cfg, bridge, port): - parent, vlan = find_bridge(cfg, bridge) - do_add_port(cfg, bridge, parent, port, vlan) - -def cmd_add_bond(cfg, bridge, port, *slaves): - parent, vlan = find_bridge(cfg, bridge) - do_add_port(cfg, bridge, parent, port, vlan) - cfg['bonding.%s.slave' % port] = list(slaves) - -def cmd_del_port(cfg, *args): - if len(args) == 2: - bridge, port = args - parent, vlan = find_bridge(cfg, bridge) - if port not in get_bridge_ports(cfg, parent, vlan): - if port in get_bridge_ports(cfg, parent, -1): - raise Error("bridge %s does not have a port %s (although its parent bridge %s does)" % (bridge, port, parent)) - else: - raise Error("bridge %s does not have a port %s" % (bridge, port)) - else: - port, = args - if not port_to_bridge(cfg, port): - raise Error("no port %s on any bridge" % port) - del_port(cfg, port) - -def cmd_port_to_br(cfg, port): - bridge = port_to_bridge(cfg, port) - if bridge: - return (bridge,) - else: - raise Error("no port named %s" % port) - -def cmd_list_ifaces(cfg, bridge): - ifaces = [] - parent, vlan = find_bridge(cfg, bridge) - for iface in get_bridge_ifaces(cfg, parent, vlan): - if iface != bridge: - ifaces.append(iface) - return ifaces - -def cmd_iface_to_br(cfg, iface): - for bridge, parent, vlan in get_bridge_info(cfg): - if iface != bridge and iface in get_bridge_ifaces(cfg, parent, vlan): - return (bridge,) - raise Error("no interface named %s" % iface) - -def cmd_br_to_vlan(cfg, bridge): - parent, vlan = find_bridge(cfg, bridge) - return (vlan,) - -def cmd_br_to_parent(cfg, bridge): - parent, vlan = find_bridge(cfg, bridge) - return (parent,) - -cmdTable = {'add-br': (cmd_add_br, True, lambda n: n == 1 or n == 3), - 'del-br': (cmd_del_br, True, 1), - 'list-br': (cmd_list_br, False, 0), - 'br-exists': (cmd_br_exists, False, 1), - 'list-ports': (cmd_list_ports, False, 1), - 'add-port': (cmd_add_port, True, 2), - 'add-bond': (cmd_add_bond, True, lambda n: n >= 4), - 'del-port': (cmd_del_port, True, lambda n: n == 1 or n == 2), - 'port-to-br': (cmd_port_to_br, False, 1), - 'br-to-vlan': (cmd_br_to_vlan, False, 1), - 'br-to-parent': (cmd_br_to_parent, False, 1), - 'list-ifaces': (cmd_list_ifaces, False, 1), - 'iface-to-br': (cmd_iface_to_br, False, 1)} - -# Break up commands at -- boundaries. -def split_commands(args): - commands = [] - command = [] - for arg in args: - if arg == '--': - if command: - commands.append(command) - command = [] - else: - command.append(arg) - if command: - commands.append(command) - return commands - -def check_command(args): - command, args = args[0], args[1:] - if command not in cmdTable: - sys.stderr.write("%s: unknown command '%s' (use --help for help)\n" - % (argv0, command)) - sys.exit(1) - - function, is_mutator, nargs = cmdTable[command] - if callable(nargs) and not nargs(len(args)): - sys.stderr.write("%s: '%s' command does not accept %d arguments (use --help for help)\n" % (argv0, command, len(args))) - sys.exit(1) - elif not callable(nargs) and len(args) != nargs: - sys.stderr.write("%s: '%s' command takes %d arguments but %d were supplied (use --help for help)\n" % (argv0, command, nargs, len(args))) - sys.exit(1) - -def run_command(cfg, args): - command, args = args[0], args[1:] - function, need_lock, nargs = cmdTable[command] - return function(cfg, *args) - -def main(): - # Parse command line. - try: - options, args = getopt.getopt(sys.argv[1:], "c:t:hV", - ["config=", - "target=", - "no-reload", - "no-syslog", - "oneline", - "help", - "version"]) - except getopt.GetoptError, msg: - sys.stderr.write("%s: %s (use --help for help)\n" % (argv0, msg)) - sys.exit(1) - - # Handle options. - oneline = False - for opt, optarg in options: - if opt == "-c" or opt == "--config": - global vswitchd_conf - vswitchd_conf = optarg - elif opt == "-t" or opt == "--target": - global vswitchd_target - vswitchd_target = optarg - elif opt == "--no-reload": - global reload_vswitchd - reload_vswitchd = False - elif opt == "-h" or opt == "--help": - usage() - elif opt == "-V" or opt == "--version": - version() - elif opt == "--no-syslog": - global enable_syslog - enable_syslog = False - elif opt == "--oneline": - oneline = True - else: - raise RuntimeError("unhandled option %s" % opt) - - if enable_syslog: - syslog.openlog("ovs-vsctl") - log("Called as %s" % ' '.join(sys.argv[1:])) - - # Break arguments into a series of commands. - commands = split_commands(args) - if not commands: - sys.stderr.write("%s: missing command name (use --help for help)\n" - % argv0) - sys.exit(1) - - # Check command syntax. - need_lock = False - for command in commands: - check_command(command) - if cmdTable[command[0]][1]: - need_lock = True - - # Execute commands. - cfg = cfg_read(vswitchd_conf, need_lock) - for command in commands: - output = run_command(cfg, command) - if oneline: - if output == None: - output = () - print '\\n'.join([str(s).replace('\\', '\\\\') - for s in output]) - elif output != None: - for line in output: - print line - if need_lock: - cfg_save(cfg, vswitchd_conf) - sys.exit(0) - -if __name__ == "__main__": - try: - main() - except Error, msg: - sys.stderr.write("%s: %s\n" % (argv0, msg.msg)) - sys.exit(1) diff --git a/vswitchd/vswitch-idl.ovsidl b/vswitchd/vswitch-idl.ovsidl index 631f6b5c..76cadaba 100644 --- a/vswitchd/vswitch-idl.ovsidl +++ b/vswitchd/vswitch-idl.ovsidl @@ -76,6 +76,9 @@ "type": "integer"}, "bond_fake_iface": { "comment": "For a bonded port, whether to create a fake interface with the name of the port. Use only for compatibility with legacy software that requires this.", + "type": "boolean"}, + "fake_bridge": { + "comment": "Does this port represent a sub-bridge for its tagged VLAN within the Bridge? See ovs-vsctl(8) for more information.", "type": "boolean"}}}, "Interface": { "comment": "An interface within a Port.", @@ -106,7 +109,8 @@ "type": {"key": "string", "value": "string", "min": 0, "max": "unlimited"}}, "ofport": { "comment": "OpenFlow port number for this interface. This is populated when the port number becomes known. Before it is populated its value will be missing. If the interface cannot be added then this is indicated by a value of -1.", - "type": {"key": "integer", "min": 0, "max": 1}}}}, + "type": {"key": "integer", "min": 0, "max": 1}, + "ephemeral": true}}}, "Mirror": { "comment": "A port mirror within a Bridge.", "columns": {