From 1b0fdca5faa8674a5e67f7435a43b361fca003cf Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Mon, 26 Sep 2011 13:07:29 -0700 Subject: [PATCH] ovs-xapi-sync: Add unit test. --- tests/MockXenAPI.py | 89 +++++++++++++++++++ tests/automake.mk | 4 +- tests/ovs-xapi-sync.at | 74 +++++++++++++++ tests/testsuite.at | 1 + ...sr_share_openvswitch_scripts_ovs-xapi-sync | 32 ++++--- 5 files changed, 189 insertions(+), 11 deletions(-) create mode 100644 tests/MockXenAPI.py create mode 100644 tests/ovs-xapi-sync.at diff --git a/tests/MockXenAPI.py b/tests/MockXenAPI.py new file mode 100644 index 000000000..f6ac22f21 --- /dev/null +++ b/tests/MockXenAPI.py @@ -0,0 +1,89 @@ +# Copyright (c) 2011 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. + +def xapi_local(): + return Session() + + +class Session(object): + def __init__(self): + self.xenapi = XenAPI() + + +class Failure(Exception): + pass + + +class XenAPI(object): + def __init__(self): + self.network = Network() + self.pool = Pool() + self.VIF = VIF() + + def login_with_password(self, unused_username, unused_password): + pass + + +class RecordRef(object): + def __init__(self, attrs): + self.attrs = attrs + + +class Table(object): + def __init__(self, records): + self.records = records + + def get_all(self): + return [RecordRef(rec) for rec in self.records] + + def get_by_uuid(self, uuid): + recs = [rec for rec in self.records if rec["uuid"] == uuid] + if len(recs) != 1: + raise Failure("No record with UUID %s" % uuid) + return RecordRef(recs[0]) + + def get_record(self, record_ref): + return record_ref.attrs + + +class Network(Table): + __records = ({"uuid": "9b66c68b-a74e-4d34-89a5-20a8ab352d1e", + "bridge": "xenbr0", + "other_config": + {"vswitch-controller-fail-mode": "secure", + "nicira-bridge-id": "custom bridge ID"}}, + {"uuid": "e1c9019d-375b-45ac-a441-0255dd2247de", + "bridge": "xenbr1", + "other_config": + {"vswitch-disable-in-band": "true"}}) + + def __init__(self): + Table.__init__(self, Network.__records) + + +class Pool(Table): + __records = ({"uuid": "7a793edf-e5f4-4994-a0f9-cee784c0cda3", + "other_config": + {"vswitch-controller-fail-mode": "secure"}},) + + def __init__(self): + Table.__init__(self, Pool.__records) + +class VIF(Table): + __records = ({"uuid": "6ab1b260-398e-49ba-827b-c7696108964c", + "other_config": + {"nicira-iface-id": "custom iface ID"}},) + + def __init__(self): + Table.__init__(self, VIF.__records) diff --git a/tests/automake.mk b/tests/automake.mk index dcf6026e4..8d019e7a0 100644 --- a/tests/automake.mk +++ b/tests/automake.mk @@ -52,6 +52,7 @@ TESTSUITE_AT = \ tests/ovsdb-idl.at \ tests/ovs-vsctl.at \ tests/ovs-monitor-ipsec.at \ + tests/ovs-xapi-sync.at \ tests/interface-reconfigure.at TESTSUITE = $(srcdir)/tests/testsuite DISTCLEANFILES += tests/atconfig tests/atlocal @@ -335,7 +336,8 @@ EXTRA_DIST += \ tests/test-json.py \ tests/test-jsonrpc.py \ tests/test-ovsdb.py \ - tests/test-reconnect.py + tests/test-reconnect.py \ + tests/MockXenAPI.py if HAVE_OPENSSL TESTPKI_FILES = \ diff --git a/tests/ovs-xapi-sync.at b/tests/ovs-xapi-sync.at new file mode 100644 index 000000000..b2bfff56f --- /dev/null +++ b/tests/ovs-xapi-sync.at @@ -0,0 +1,74 @@ +AT_BANNER([ovs-xapi-sync]) + +AT_SETUP([ovs-xapi-sync]) +AT_SKIP_IF([test $HAVE_PYTHON = no]) + +# Mock up the XenAPI. +cp "$top_srcdir/tests/MockXenAPI.py" XenAPI.py +PYTHONPATH=`pwd`:$PYTHONPATH +export PYTHONPATH + +OVS_PKGDATADIR=`pwd`; export OVS_PKGDATADIR +cp "$top_srcdir/vswitchd/vswitch.ovsschema" . + +cp "$top_srcdir/xenserver/usr_share_openvswitch_scripts_ovs-xapi-sync" \ + ovs-xapi-sync + +trap 'kill `cat pid ovs-xapi-sync.pid`' 0 + +mkdir var var/run +touch var/run/xapi_init_complete.cookie + +ovs_vsctl () { + ovs-vsctl --timeout=5 --no-wait -vreconnect:ANY:emer --db=unix:socket "$@" +} + +# Start ovsdb-server. +OVS_VSCTL_SETUP + +# Start ovs-xapi-sync. +AT_CHECK([$PYTHON ./ovs-xapi-sync "--pidfile-name=`pwd`/ovs-xapi-sync.pid" \ + "--root-prefix=`pwd`" unix:socket >log 2>&1 &]) +AT_CAPTURE_FILE([log]) + +# Add bridges and check ovs-xapi-sync's work. +AT_CHECK([ovs_vsctl -- add-br xenbr0 -- add-br xenbr1]) +OVS_WAIT_UNTIL([test "X`ovs_vsctl get bridge xenbr0 fail-mode`" != "X[[]]"]) +AT_CHECK([ovs_vsctl \ + -- get bridge xenbr0 fail-mode other-config external-ids \ + -- get bridge xenbr1 fail-mode other-config external-ids], [0], + [[secure +{} +{bridge-id="custom bridge ID"} +secure +{disable-in-band="true"} +{} +]]) + +# Add vif and check daemon's work. +AT_CHECK([ovs_vsctl \ + -- add-port xenbr0 vif1.0 \ + -- set Interface vif1.0 'external-ids={attached-mac="00:11:22:33:44:55", xs-network-uuid="9b66c68b-a74e-4d34-89a5-20a8ab352d1e", xs-vif-uuid="6ab1b260-398e-49ba-827b-c7696108964c", xs-vm-uuid="fcb8a3f6-dc04-41d2-8b8a-55afd2b755b8"'}]) +OVS_WAIT_UNTIL([ovs_vsctl get interface vif1.0 external-ids:iface-id >/dev/null 2>&1]) +AT_CHECK([ovs_vsctl get interface vif1.0 external-ids], [0], + [{attached-mac="00:11:22:33:44:55", iface-id="custom iface ID", iface-status=active, xs-network-uuid="9b66c68b-a74e-4d34-89a5-20a8ab352d1e", xs-vif-uuid="6ab1b260-398e-49ba-827b-c7696108964c", xs-vm-uuid="fcb8a3f6-dc04-41d2-8b8a-55afd2b755b8"} +]) + +# Add corresponding tap and check daemon's work. +AT_CHECK([ovs_vsctl add-port xenbr0 tap1.0]) +OVS_WAIT_UNTIL([ovs_vsctl get interface tap1.0 external-ids:iface-id >/dev/null 2>&1]) +AT_CHECK([ovs_vsctl \ + -- get interface vif1.0 external-ids \ + -- get interface tap1.0 external-ids], [0], + [{attached-mac="00:11:22:33:44:55", iface-id="custom iface ID", iface-status=inactive, xs-network-uuid="9b66c68b-a74e-4d34-89a5-20a8ab352d1e", xs-vif-uuid="6ab1b260-398e-49ba-827b-c7696108964c", xs-vm-uuid="fcb8a3f6-dc04-41d2-8b8a-55afd2b755b8"} +{attached-mac="00:11:22:33:44:55", iface-id="custom iface ID", iface-status=active, xs-network-uuid="9b66c68b-a74e-4d34-89a5-20a8ab352d1e", xs-vif-uuid="6ab1b260-398e-49ba-827b-c7696108964c", xs-vm-uuid="fcb8a3f6-dc04-41d2-8b8a-55afd2b755b8"} +]) + +# Remove corresponding tap and check daemon's work. +AT_CHECK([ovs_vsctl del-port tap1.0]) +OVS_WAIT_UNTIL([test `ovs_vsctl get interface vif1.0 external-ids:iface-status` = active]) +AT_CHECK([ovs_vsctl get interface vif1.0 external-ids], [0], + [{attached-mac="00:11:22:33:44:55", iface-id="custom iface ID", iface-status=active, xs-network-uuid="9b66c68b-a74e-4d34-89a5-20a8ab352d1e", xs-vif-uuid="6ab1b260-398e-49ba-827b-c7696108964c", xs-vm-uuid="fcb8a3f6-dc04-41d2-8b8a-55afd2b755b8"} +]) + +AT_CLEANUP diff --git a/tests/testsuite.at b/tests/testsuite.at index 19b7802db..86085724e 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -65,4 +65,5 @@ m4_include([tests/ofproto-dpif.at]) m4_include([tests/ovsdb.at]) m4_include([tests/ovs-vsctl.at]) m4_include([tests/ovs-monitor-ipsec.at]) +m4_include([tests/ovs-xapi-sync.at]) m4_include([tests/interface-reconfigure.at]) diff --git a/xenserver/usr_share_openvswitch_scripts_ovs-xapi-sync b/xenserver/usr_share_openvswitch_scripts_ovs-xapi-sync index 7109609ec..f45865446 100755 --- a/xenserver/usr_share_openvswitch_scripts_ovs-xapi-sync +++ b/xenserver/usr_share_openvswitch_scripts_ovs-xapi-sync @@ -40,6 +40,7 @@ import ovs.util import ovs.daemon import ovs.db.idl +root_prefix = '' # Prefix for absolute file names, for testing. s_log = logging.getLogger("ovs-xapi-sync") vsctl = "/usr/bin/ovs-vsctl" session = None @@ -59,9 +60,9 @@ def init_session(): try: session = XenAPI.xapi_local() session.xenapi.login_with_password("", "") - except: + except XenAPI.Failure, e: session = None - s_log.warning("Couldn't login to XAPI") + s_log.warning("Couldn't login to XAPI (%s)" % e) return False return True @@ -218,8 +219,10 @@ def usage(): print "usage: %s [OPTIONS] DATABASE" % sys.argv[0] print "where DATABASE is a socket on which ovsdb-server is listening." ovs.daemon.usage() - print "Other options:" - print " -h, --help display this help message" + print """\ +Other options: + --root-prefix=DIR Use DIR as alternate root directory (for testing). + -h, --help display this help message""" sys.exit(0) @@ -232,16 +235,21 @@ def handler(signum, _): def main(argv): global force_run - l_handler = logging.handlers.RotatingFileHandler( + s_log.addHandler(logging.StreamHandler()) + try: + l_handler = logging.handlers.RotatingFileHandler( "/var/log/openvswitch/ovs-xapi-sync.log") - l_formatter = logging.Formatter('%(filename)s: %(levelname)s: %(message)s') - l_handler.setFormatter(l_formatter) - s_log.addHandler(l_handler) + l_formatter = logging.Formatter('%(filename)s: %(levelname)s: %(message)s') + l_handler.setFormatter(l_formatter) + s_log.addHandler(l_handler) + except IOError, e: + logging.basicConfig() + s_log.warn("failed to open logfile (%s)" % e) s_log.setLevel(logging.INFO) try: options, args = getopt.gnu_getopt( - argv[1:], 'h', ['help'] + ovs.daemon.LONG_OPTIONS) + argv[1:], 'h', ['help', 'root-prefix='] + ovs.daemon.LONG_OPTIONS) except getopt.GetoptError, geo: sys.stderr.write("%s: %s\n" % (ovs.util.PROGRAM_NAME, geo.msg)) sys.exit(1) @@ -249,6 +257,9 @@ def main(argv): for key, value in options: if key in ['-h', '--help']: usage() + elif key == '--root-prefix': + global root_prefix + root_prefix = value elif not ovs.daemon.parse_opt(key, value): sys.stderr.write("%s: unhandled option %s\n" % (ovs.util.PROGRAM_NAME, key)) @@ -269,7 +280,8 @@ def main(argv): # This daemon is usually started before XAPI, but to complete our # tasks, we need it. Wait here until it's up. - while not os.path.exists("/var/run/xapi_init_complete.cookie"): + cookie_file = root_prefix + "/var/run/xapi_init_complete.cookie" + while not os.path.exists(cookie_file): time.sleep(1) signal.signal(signal.SIGHUP, handler) -- 2.43.0