From f4a0c2f879ff351ce87f45d1e761b7a5964acac9 Mon Sep 17 00:00:00 2001 From: Tommi Virtanen Date: Tue, 11 Jan 2011 16:43:46 -0800 Subject: [PATCH] Add CLI tests for osdmaptool and friends. Uses a python package "cram" as test runner. Requires PIP (python-pip.deb) installed on the build machine, to actually run these tests. The cram application itself is included as a tarball that gets installed in a virtualenv when the tests are run. cram is GPL. --- Makefile.am | 9 ++- src/test/.gitignore | 1 + src/test/cli/cauthtool/add-key-segv.t | 18 ++++++ src/test/cli/cauthtool/add-key.t | 11 ++++ src/test/cli/cauthtool/cap-invalid.t | 12 ++++ src/test/cli/cauthtool/cap-overwrite.t | 11 ++++ src/test/cli/cauthtool/cap.t | 6 ++ src/test/cli/cauthtool/create-gen-list.t | 18 ++++++ src/test/cli/cauthtool/help.t | 5 ++ src/test/cli/cauthtool/list-empty.t | 9 +++ src/test/cli/cauthtool/list-nonexistent.t | 7 ++ src/test/cli/cauthtool/manpage.t | 32 +++++++++ src/test/cli/cauthtool/simple.t | 4 ++ src/test/cli/cconf/help.t | 33 ++++++++++ src/test/cli/cconf/invalid-args.t | 22 +++++++ src/test/cli/cconf/manpage.t | 35 ++++++++++ src/test/cli/cconf/option.t | 38 +++++++++++ src/test/cli/cconf/sections.t | 18 ++++++ src/test/cli/cconf/simple.t | 13 ++++ src/test/cli/monmaptool/add-exists.t | 28 ++++++++ src/test/cli/monmaptool/add-many.t | 31 +++++++++ src/test/cli/monmaptool/clobber.t | 40 ++++++++++++ src/test/cli/monmaptool/create-print.t | 13 ++++ src/test/cli/monmaptool/create-with-add.t | 14 ++++ src/test/cli/monmaptool/help.t | 7 ++ src/test/cli/monmaptool/print-empty.t | 5 ++ src/test/cli/monmaptool/print-nonexistent.t | 6 ++ src/test/cli/monmaptool/rm-nonexistent.t | 26 ++++++++ src/test/cli/monmaptool/rm.t | 23 +++++++ src/test/cli/monmaptool/simple.t | 3 + src/test/cli/osdmaptool/clobber.t | 68 ++++++++++++++++++++ src/test/cli/osdmaptool/create-print.t | 26 ++++++++ src/test/cli/osdmaptool/help.t | 7 ++ src/test/cli/osdmaptool/print-empty.t | 5 ++ src/test/cli/osdmaptool/print-nonexistent.t | 5 ++ src/test/cli/osdmaptool/simple.t | 7 ++ src/test/downloads/cram-0.5.tar.gz | Bin 0 -> 21815 bytes src/test/run-cli-tests | 22 +++++++ 38 files changed, 636 insertions(+), 2 deletions(-) create mode 100644 src/test/.gitignore create mode 100644 src/test/cli/cauthtool/add-key-segv.t create mode 100644 src/test/cli/cauthtool/add-key.t create mode 100644 src/test/cli/cauthtool/cap-invalid.t create mode 100644 src/test/cli/cauthtool/cap-overwrite.t create mode 100644 src/test/cli/cauthtool/cap.t create mode 100644 src/test/cli/cauthtool/create-gen-list.t create mode 100644 src/test/cli/cauthtool/help.t create mode 100644 src/test/cli/cauthtool/list-empty.t create mode 100644 src/test/cli/cauthtool/list-nonexistent.t create mode 100644 src/test/cli/cauthtool/manpage.t create mode 100644 src/test/cli/cauthtool/simple.t create mode 100644 src/test/cli/cconf/help.t create mode 100644 src/test/cli/cconf/invalid-args.t create mode 100644 src/test/cli/cconf/manpage.t create mode 100644 src/test/cli/cconf/option.t create mode 100644 src/test/cli/cconf/sections.t create mode 100644 src/test/cli/cconf/simple.t create mode 100644 src/test/cli/monmaptool/add-exists.t create mode 100644 src/test/cli/monmaptool/add-many.t create mode 100644 src/test/cli/monmaptool/clobber.t create mode 100644 src/test/cli/monmaptool/create-print.t create mode 100644 src/test/cli/monmaptool/create-with-add.t create mode 100644 src/test/cli/monmaptool/help.t create mode 100644 src/test/cli/monmaptool/print-empty.t create mode 100644 src/test/cli/monmaptool/print-nonexistent.t create mode 100644 src/test/cli/monmaptool/rm-nonexistent.t create mode 100644 src/test/cli/monmaptool/rm.t create mode 100644 src/test/cli/monmaptool/simple.t create mode 100644 src/test/cli/osdmaptool/clobber.t create mode 100644 src/test/cli/osdmaptool/create-print.t create mode 100644 src/test/cli/osdmaptool/help.t create mode 100644 src/test/cli/osdmaptool/print-empty.t create mode 100644 src/test/cli/osdmaptool/print-nonexistent.t create mode 100644 src/test/cli/osdmaptool/simple.t create mode 100644 src/test/downloads/cram-0.5.tar.gz create mode 100755 src/test/run-cli-tests diff --git a/Makefile.am b/Makefile.am index 8879c60f4cc1c..629ad9cb936ba 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4,12 +4,17 @@ EXTRA_DIST = autogen.sh ceph.spec.in SUBDIRS = . src man -# Build gtest before we build our own tests. Doing this instead of -# SUBDIRS gtest's own tests being run and slowing us down. check-local: +# Build gtest before we build our own tests. Doing this +# instead of SUBDIRS gtest's own tests being run and slowing +# us down. @echo "Making lib/libgtest.a lib/libgtest_main.a in src/gtest" @cd src/gtest && $(MAKE) $(AM_MAKEFLAGS) lib/libgtest.la lib/libgtest_main.la +# exercise cli tools + ./src/test/run-cli-tests + + # "make distclean" both runs this and recurses into src/gtest, if # gtest is in DIST_SUBDIRS. Take extra care to not fail when # effectively cleaned twice. diff --git a/src/test/.gitignore b/src/test/.gitignore new file mode 100644 index 0000000000000..80c7a7556453f --- /dev/null +++ b/src/test/.gitignore @@ -0,0 +1 @@ +/virtualenv diff --git a/src/test/cli/cauthtool/add-key-segv.t b/src/test/cli/cauthtool/add-key-segv.t new file mode 100644 index 0000000000000..370dbabdf04cf --- /dev/null +++ b/src/test/cli/cauthtool/add-key-segv.t @@ -0,0 +1,18 @@ + $ cauthtool kring --create-keyring + creating kring + +# TODO fix me + $ cauthtool kring --add-key 'FAKEBASE64 foo' + *** Caught signal (Segmentation fault) *** + in thread [0-9a-f]{12} (re) + ceph version .* (re) + 1: .* (re) + 2: .* (re) + 3: .* (re) + 4: .* (re) + 5: .* (re) + 6: .* (re) + 7: .* (re) + 8: .* (re) + Segmentation fault + [139] diff --git a/src/test/cli/cauthtool/add-key.t b/src/test/cli/cauthtool/add-key.t new file mode 100644 index 0000000000000..19cf81ce28ce5 --- /dev/null +++ b/src/test/cli/cauthtool/add-key.t @@ -0,0 +1,11 @@ + $ cauthtool kring --create-keyring + creating kring + + $ cauthtool kring --add-key 'AQAK7yxNeF+nHBAA0SgSdbs8IkJrxroDeJ6SwQ== 18446744073709551615' + added entity client.admin auth auth(auid = 18446744073709551615 key=AQAK7yxNeF+nHBAA0SgSdbs8IkJrxroDeJ6SwQ== with 0 caps) + +# cram makes matching escape-containing lines with regexps a bit ugly + $ cauthtool kring --list + client.admin + \\t key: AQAK7yxNeF\+nHBAA0SgSdbs8IkJrxroDeJ6SwQ== \(esc\) (re) + \\tauid: 18446744073709551615 \(esc\) (re) diff --git a/src/test/cli/cauthtool/cap-invalid.t b/src/test/cli/cauthtool/cap-invalid.t new file mode 100644 index 0000000000000..3776e3ca52221 --- /dev/null +++ b/src/test/cli/cauthtool/cap-invalid.t @@ -0,0 +1,12 @@ + $ cauthtool kring --create-keyring --gen-key + creating kring + +# TODO is this nice? + $ cauthtool --cap osd 'broken' kring + $ cauthtool kring --list|grep caps: + \tcaps: [osd] broken (esc) + +# TODO is this nice? + $ cauthtool --cap xyzzy 'broken' kring + $ cauthtool kring --list|grep caps: + \tcaps: [xyzzy] broken (esc) diff --git a/src/test/cli/cauthtool/cap-overwrite.t b/src/test/cli/cauthtool/cap-overwrite.t new file mode 100644 index 0000000000000..7a88298fc70c6 --- /dev/null +++ b/src/test/cli/cauthtool/cap-overwrite.t @@ -0,0 +1,11 @@ + $ cauthtool kring --create-keyring --gen-key + creating kring + + $ cauthtool --cap osd 'allow rx pool=swimming' kring + $ cauthtool kring --list|grep caps: + \tcaps: [osd] allow rx pool=swimming (esc) + +# TODO it seems --cap overwrites all previous caps; is this wanted? + $ cauthtool --cap mds 'allow' kring + $ cauthtool kring --list|grep caps: + \tcaps: [mds] allow (esc) diff --git a/src/test/cli/cauthtool/cap.t b/src/test/cli/cauthtool/cap.t new file mode 100644 index 0000000000000..0c2d5d3cb8557 --- /dev/null +++ b/src/test/cli/cauthtool/cap.t @@ -0,0 +1,6 @@ + $ cauthtool kring --create-keyring --gen-key + creating kring + + $ cauthtool --cap osd 'allow rx pool=swimming' kring + $ cauthtool kring --list|grep caps: + \tcaps: [osd] allow rx pool=swimming (esc) diff --git a/src/test/cli/cauthtool/create-gen-list.t b/src/test/cli/cauthtool/create-gen-list.t new file mode 100644 index 0000000000000..67fe0d2021cb9 --- /dev/null +++ b/src/test/cli/cauthtool/create-gen-list.t @@ -0,0 +1,18 @@ + $ cauthtool kring --create-keyring + creating kring + + $ cauthtool kring --list + + $ cauthtool kring --gen-key + +# cram makes matching escape-containing lines with regexps a bit ugly + $ cauthtool kring --list + client.admin + \\t key: [a-zA-Z0-9+/]+=* \(esc\) (re) + \\tauid: [0-9]{20} \(esc\) (re) + +# synonym + $ cauthtool kring -l + client.admin + \\t key: [a-zA-Z0-9+/]+=* \(esc\) (re) + \\tauid: [0-9]{20} \(esc\) (re) diff --git a/src/test/cli/cauthtool/help.t b/src/test/cli/cauthtool/help.t new file mode 100644 index 0000000000000..339e2cd3f91d6 --- /dev/null +++ b/src/test/cli/cauthtool/help.t @@ -0,0 +1,5 @@ +# TODO synchronize with man page + $ cauthtool --help + no command specified + usage: [--create-keyring] [--gen-key] [--name=] [--set-uid=uid] [--caps=] [--list] [--print-key] + [1] diff --git a/src/test/cli/cauthtool/list-empty.t b/src/test/cli/cauthtool/list-empty.t new file mode 100644 index 0000000000000..e19e769adf4f5 --- /dev/null +++ b/src/test/cli/cauthtool/list-empty.t @@ -0,0 +1,9 @@ + $ touch empty + + $ cauthtool --list empty + error reading file empty + [1] + + $ cauthtool -l empty + error reading file empty + [1] diff --git a/src/test/cli/cauthtool/list-nonexistent.t b/src/test/cli/cauthtool/list-nonexistent.t new file mode 100644 index 0000000000000..68308cce833a2 --- /dev/null +++ b/src/test/cli/cauthtool/list-nonexistent.t @@ -0,0 +1,7 @@ + $ cauthtool --list nonexistent + can't open nonexistent: No such file or directory + [1] + + $ cauthtool -l nonexistent + can't open nonexistent: No such file or directory + [1] diff --git a/src/test/cli/cauthtool/manpage.t b/src/test/cli/cauthtool/manpage.t new file mode 100644 index 0000000000000..3417755b1df5b --- /dev/null +++ b/src/test/cli/cauthtool/manpage.t @@ -0,0 +1,32 @@ +# demonstrate that manpage examples fail without config +# TODO fix the manpage + $ cauthtool -c -n client.foo --gen-key keyring.bin + error reading config file(s) /etc/ceph/ceph.conf, ~/.ceph/config, ceph.conf + [1] + +# work around the above + $ touch ceph.conf + +To create a new keyring containing a key for client.foo: + +#TODO apparently -c is not enough for --create-keyring; fix manpage + $ cauthtool -c -n client.foo --gen-key keyring.bin + can't open keyring.bin: No such file or directory + [1] + + $ cauthtool --create-keyring -n client.foo --gen-key keyring.bin + creating keyring.bin + +To associate some capabilities with the key (namely, the ability to mount a Ceph filesystem): + + $ cauthtool -n client.foo --cap mds 'allow' --cap osd 'allow rw pool=data' --cap mon 'allow r' keyring.bin + +To display the contents of the keyring: + + $ cauthtool -l keyring.bin + client.foo + \\t key: [a-zA-Z0-9+/]+=* \(esc\) (re) + \\tauid: [0-9]{20} \(esc\) (re) + \tcaps: [mds] allow (esc) + \tcaps: [mon] allow r (esc) + \tcaps: [osd] allow rw pool=data (esc) diff --git a/src/test/cli/cauthtool/simple.t b/src/test/cli/cauthtool/simple.t new file mode 100644 index 0000000000000..f016e91362ad6 --- /dev/null +++ b/src/test/cli/cauthtool/simple.t @@ -0,0 +1,4 @@ + $ cauthtool + cauthtool: must specify filename + usage: [--create-keyring] [--gen-key] [--name=] [--set-uid=uid] [--caps=] [--list] [--print-key] + [1] diff --git a/src/test/cli/cconf/help.t b/src/test/cli/cconf/help.t new file mode 100644 index 0000000000000..4892ac92ca97a --- /dev/null +++ b/src/test/cli/cconf/help.t @@ -0,0 +1,33 @@ +#TODO + $ cconf --help + Parse error at argument: --help + Ceph configuration query tool + + USAGE + cconf + + ACTIONS + -l|--list-sections List sections in prefix + + --lookup [defval] Print a configuration setting to stdout. + \t\t\t\t If the setting is not defined, and the (esc) + \t\t\t\t optional argument defval is provide, it will (esc) + \t\t\t\t be printed instead. variables in defval are (esc) + \t\t\t\t interpolated. (esc) + + FLAGS + -i id Set id + [-s
] Add to list of sections to search + + If there is no action given, the action will default to --lookup. + + EXAMPLES + [$] cconf -i cconf -c /etc/ceph/ceph\.conf -t mon -i 0 'mon addr' (re) + Find out if there is a 'mon addr' defined in /etc/ceph/ceph.conf + + [$] cconf -l mon (re) + List sections beginning with 'mon'. + + RETURN CODE + Return code will be 0 on success; error code otherwise. + [1] diff --git a/src/test/cli/cconf/invalid-args.t b/src/test/cli/cconf/invalid-args.t new file mode 100644 index 0000000000000..b4e4ae26d95e3 --- /dev/null +++ b/src/test/cli/cconf/invalid-args.t @@ -0,0 +1,22 @@ + $ cat >test.conf < [bar] + > bar = green + > EOF + +# TODO output an error + $ cconf -c test.conf broken + [1] + +# TODO output an error (missing key) + $ cconf -c test.conf -s bar + *** Caught signal (Segmentation fault) *** + in thread [0-9a-f]{12} (re) + ceph version .* (re) + 1: .* (re) + 2: .* (re) + 3: .* (re) + 4: .* (re) + 5: .* (re) + 6: .* (re) + Segmentation fault + [139] diff --git a/src/test/cli/cconf/manpage.t b/src/test/cli/cconf/manpage.t new file mode 100644 index 0000000000000..bb436a771fca8 --- /dev/null +++ b/src/test/cli/cconf/manpage.t @@ -0,0 +1,35 @@ +# setup + $ cat >foo.conf <<'EOF' + > ; --------------------- + > [group cephnet] + > addr = 10.3.14.0/24 + > + > [global] + > pid file = /home/sage/ceph/src/out/$name.pid + > + > [osd] + > osd data = /mnt/osd$id + > [osd3] + > host = cosd3 + > EOF + +To extract the value of the "osd data" option for the osd0 daemon, + +# TODO shouldn't this say /mnt/osd0? is this the fault of cram? + + $ cconf -c foo.conf "osd data" -i 0 -t osd + /mnt/osd0 + +This is equivalent to doing specifying sections [osd0], [osd.0], +[osd], or [global], in that order of preference: + +# TODO the "admin" here seems like an actual bug + + $ cconf -c foo.conf "osd data" -s osd0 -s osd.0 -s osd -s global + /mnt/osdadmin + +To list all sections that begin with osd: + + $ cconf -c foo.conf -l osd + osd + osd3 diff --git a/src/test/cli/cconf/option.t b/src/test/cli/cconf/option.t new file mode 100644 index 0000000000000..d16b42e9ccfb5 --- /dev/null +++ b/src/test/cli/cconf/option.t @@ -0,0 +1,38 @@ + $ cat >test.conf < [bar] + > bar = green + > [foo] + > bar = blue + > [baz] + > bar = yellow + > [thud] + > bar = red + > [nobar] + > other = 42 + > EOF + + $ cconf -c test.conf bar -s foo + blue + +# TODO man page stops in the middle of a sentence + + $ cconf -c test.conf bar -s xyzzy + [1] + + $ cconf -c test.conf bar notfound -s xyzzy + notfound + + $ cconf -c test.conf bar notfound -s xyzzy -s thud + red + + $ cconf -c test.conf bar notfound -s nobar -s thud + red + + $ cconf -c test.conf bar notfound -s thud -s baz + red + + $ cconf -c test.conf bar notfound -s baz -s thud + yellow + + $ cconf -c test.conf bar notfound -s xyzzy -s nobar -s thud -s baz + red diff --git a/src/test/cli/cconf/sections.t b/src/test/cli/cconf/sections.t new file mode 100644 index 0000000000000..b4f26da95a6a4 --- /dev/null +++ b/src/test/cli/cconf/sections.t @@ -0,0 +1,18 @@ + $ cat >test.conf < [bar] + > bar = green + > [foo] + > bar = blue + > [baz] + > bar = yellow + > [thud] + > bar = yellow + > EOF + + $ cconf -c test.conf -l bar + bar + + $ cconf -c test.conf -l b + bar + baz + diff --git a/src/test/cli/cconf/simple.t b/src/test/cli/cconf/simple.t new file mode 100644 index 0000000000000..39d2bc64d348b --- /dev/null +++ b/src/test/cli/cconf/simple.t @@ -0,0 +1,13 @@ +#TODO + $ cconf + *** Caught signal (Segmentation fault) *** + in thread [0-9a-f]{12} (re) + ceph version .* (re) + 1: .* (re) + 2: .* (re) + 3: .* (re) + 4: .* (re) + 5: .* (re) + 6: .* (re) + Segmentation fault + [139] diff --git a/src/test/cli/monmaptool/add-exists.t b/src/test/cli/monmaptool/add-exists.t new file mode 100644 index 0000000000000..5ea31af704a81 --- /dev/null +++ b/src/test/cli/monmaptool/add-exists.t @@ -0,0 +1,28 @@ + $ monmaptool --create mymonmap + monmaptool: monmap file mymonmap + failed to open log file '/var/log/ceph/': error 21: Is a directory + \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ [0-9a-f]{12} can't open mymonmap: error 2: No such file or directory (re) + monmaptool: generated fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re) + monmaptool: writing epoch 1 to mymonmap (0 monitors) + + $ ORIG_FSID="$(monmaptool --print mymonmap|grep ^fsid)" + + $ monmaptool --add foo 2.3.4.5:6789 mymonmap + monmaptool: monmap file mymonmap + monmaptool: writing epoch 2 to mymonmap (1 monitors) + $ monmaptool --add foo 3.4.5.6:7890 mymonmap + monmaptool: monmap file mymonmap + monmaptool: map already contains mon.foo + usage: [--print] [--create [--clobber]] [--add name 1.2.3.4:567] [--rm name] + [1] + + $ monmaptool --print mymonmap + monmaptool: monmap file mymonmap + epoch 2 + fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re) + last_changed \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re) + created \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re) + 0: 2.3.4.5:6789/0 mon.foo + + $ NEW_FSID="$(monmaptool --print mymonmap|grep ^fsid)" + $ [ "$ORIG_FSID" = "$NEW_FSID" ] diff --git a/src/test/cli/monmaptool/add-many.t b/src/test/cli/monmaptool/add-many.t new file mode 100644 index 0000000000000..1c75c8946f233 --- /dev/null +++ b/src/test/cli/monmaptool/add-many.t @@ -0,0 +1,31 @@ + $ monmaptool --create mymonmap + monmaptool: monmap file mymonmap + failed to open log file '/var/log/ceph/': error 21: Is a directory + \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ [0-9a-f]{12} can't open mymonmap: error 2: No such file or directory (re) + monmaptool: generated fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re) + monmaptool: writing epoch 1 to mymonmap (0 monitors) + + $ ORIG_FSID="$(monmaptool --print mymonmap|grep ^fsid)" + + $ monmaptool --add foo 2.3.4.5:6789 mymonmap + monmaptool: monmap file mymonmap + monmaptool: writing epoch 2 to mymonmap (1 monitors) + $ monmaptool --add bar 3.4.5.6:7890 mymonmap + monmaptool: monmap file mymonmap + monmaptool: writing epoch 3 to mymonmap (2 monitors) + $ monmaptool --add baz 4.5.6.7:8901 mymonmap + monmaptool: monmap file mymonmap + monmaptool: writing epoch 4 to mymonmap (3 monitors) + + $ monmaptool --print mymonmap + monmaptool: monmap file mymonmap + epoch 4 + fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re) + last_changed \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re) + created \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re) + 0: 3.4.5.6:7890/0 mon.bar + 1: 4.5.6.7:8901/0 mon.baz + 2: 2.3.4.5:6789/0 mon.foo + + $ NEW_FSID="$(monmaptool --print mymonmap|grep ^fsid)" + $ [ "$ORIG_FSID" = "$NEW_FSID" ] diff --git a/src/test/cli/monmaptool/clobber.t b/src/test/cli/monmaptool/clobber.t new file mode 100644 index 0000000000000..1bb27a7c2b333 --- /dev/null +++ b/src/test/cli/monmaptool/clobber.t @@ -0,0 +1,40 @@ + $ monmaptool --create --add foo 2.3.4.5:6789 mymonmap + monmaptool: monmap file mymonmap + failed to open log file '/var/log/ceph/': error 21: Is a directory + \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ [0-9a-f]{12} can't open mymonmap: error 2: No such file or directory (re) + monmaptool: generated fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re) + monmaptool: writing epoch 1 to mymonmap (1 monitors) + + $ ORIG_FSID="$(monmaptool --print mymonmap|grep ^fsid)" + + $ monmaptool --create mymonmap + monmaptool: monmap file mymonmap + monmaptool: mymonmap exists, --clobber to overwrite + [255] + +# hasn't changed yet + $ monmaptool --print mymonmap + monmaptool: monmap file mymonmap + epoch 1 + fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re) + last_changed \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re) + created \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re) + 0: 2.3.4.5:6789/0 mon.foo + + $ NEW_FSID="$(monmaptool --print mymonmap|grep ^fsid)" + $ [ "$ORIG_FSID" = "$NEW_FSID" ] + + $ monmaptool --create --clobber mymonmap + monmaptool: monmap file mymonmap + monmaptool: generated fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re) + monmaptool: writing epoch 1 to mymonmap (0 monitors) + + $ NEW_FSID="$(monmaptool --print mymonmap|grep ^fsid)" + $ [ "$ORIG_FSID" != "$NEW_FSID" ] + + $ monmaptool --print mymonmap + monmaptool: monmap file mymonmap + epoch 1 + fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re) + last_changed \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re) + created \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re) diff --git a/src/test/cli/monmaptool/create-print.t b/src/test/cli/monmaptool/create-print.t new file mode 100644 index 0000000000000..e8aca1012da6b --- /dev/null +++ b/src/test/cli/monmaptool/create-print.t @@ -0,0 +1,13 @@ + $ monmaptool --create mymonmap + monmaptool: monmap file mymonmap + failed to open log file '/var/log/ceph/': error 21: Is a directory + \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ [0-9a-f]{12} can't open mymonmap: error 2: No such file or directory (re) + monmaptool: generated fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re) + monmaptool: writing epoch 1 to mymonmap (0 monitors) + + $ monmaptool --print mymonmap + monmaptool: monmap file mymonmap + epoch 1 + fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re) + last_changed \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re) + created \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re) diff --git a/src/test/cli/monmaptool/create-with-add.t b/src/test/cli/monmaptool/create-with-add.t new file mode 100644 index 0000000000000..e944905a666ae --- /dev/null +++ b/src/test/cli/monmaptool/create-with-add.t @@ -0,0 +1,14 @@ + $ monmaptool --create --add foo 2.3.4.5:6789 mymonmap + monmaptool: monmap file mymonmap + failed to open log file '/var/log/ceph/': error 21: Is a directory + \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ [0-9a-f]{12} can't open mymonmap: error 2: No such file or directory (re) + monmaptool: generated fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re) + monmaptool: writing epoch 1 to mymonmap (1 monitors) + + $ monmaptool --print mymonmap + monmaptool: monmap file mymonmap + epoch 1 + fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re) + last_changed \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re) + created \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re) + 0: 2.3.4.5:6789/0 mon.foo diff --git a/src/test/cli/monmaptool/help.t b/src/test/cli/monmaptool/help.t new file mode 100644 index 0000000000000..8b54aa23524ec --- /dev/null +++ b/src/test/cli/monmaptool/help.t @@ -0,0 +1,7 @@ +# TODO be user-friendly + $ monmaptool --help + monmaptool: monmap file --help + failed to open log file '/var/log/ceph/': error 21: Is a directory + \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ [0-9a-f]{12} can't open --help: error 2: No such file or directory (re) + monmaptool: couldn't open --help: Is a directory + [255] diff --git a/src/test/cli/monmaptool/print-empty.t b/src/test/cli/monmaptool/print-empty.t new file mode 100644 index 0000000000000..cd67db485806d --- /dev/null +++ b/src/test/cli/monmaptool/print-empty.t @@ -0,0 +1,5 @@ + $ touch empty + $ monmaptool --print empty + monmaptool: monmap file empty + monmaptool: unable to read monmap file + [255] diff --git a/src/test/cli/monmaptool/print-nonexistent.t b/src/test/cli/monmaptool/print-nonexistent.t new file mode 100644 index 0000000000000..46fd04b734479 --- /dev/null +++ b/src/test/cli/monmaptool/print-nonexistent.t @@ -0,0 +1,6 @@ + $ monmaptool --print nonexistent + monmaptool: monmap file nonexistent + failed to open log file '/var/log/ceph/': error 21: Is a directory + \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ [0-9a-f]{12} can't open nonexistent: error 2: No such file or directory (re) + monmaptool: couldn't open nonexistent: Is a directory + [255] diff --git a/src/test/cli/monmaptool/rm-nonexistent.t b/src/test/cli/monmaptool/rm-nonexistent.t new file mode 100644 index 0000000000000..183b83e7937eb --- /dev/null +++ b/src/test/cli/monmaptool/rm-nonexistent.t @@ -0,0 +1,26 @@ + $ monmaptool --create --add foo 2.3.4.5:6789 mymonmap + monmaptool: monmap file mymonmap + failed to open log file '/var/log/ceph/': error 21: Is a directory + \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ [0-9a-f]{12} can't open mymonmap: error 2: No such file or directory (re) + monmaptool: generated fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re) + monmaptool: writing epoch 1 to mymonmap (1 monitors) + + $ ORIG_FSID="$(monmaptool --print mymonmap|grep ^fsid)" + + $ monmaptool --rm doesnotexist mymonmap + monmaptool: monmap file mymonmap + monmaptool: removing doesnotexist + monmaptool: map does not contain doesnotexist + usage: [--print] [--create [--clobber]] [--add name 1.2.3.4:567] [--rm name] + [1] + + $ monmaptool --print mymonmap + monmaptool: monmap file mymonmap + epoch 1 + fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re) + last_changed \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re) + created \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re) + 0: 2.3.4.5:6789/0 mon.foo + + $ NEW_FSID="$(monmaptool --print mymonmap|grep ^fsid)" + $ [ "$ORIG_FSID" = "$NEW_FSID" ] diff --git a/src/test/cli/monmaptool/rm.t b/src/test/cli/monmaptool/rm.t new file mode 100644 index 0000000000000..8e7a1e0733a99 --- /dev/null +++ b/src/test/cli/monmaptool/rm.t @@ -0,0 +1,23 @@ + $ monmaptool --create --add foo 2.3.4.5:6789 mymonmap + monmaptool: monmap file mymonmap + failed to open log file '/var/log/ceph/': error 21: Is a directory + \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ [0-9a-f]{12} can't open mymonmap: error 2: No such file or directory (re) + monmaptool: generated fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re) + monmaptool: writing epoch 1 to mymonmap (1 monitors) + + $ ORIG_FSID="$(monmaptool --print mymonmap|grep ^fsid)" + + $ monmaptool --rm foo mymonmap + monmaptool: monmap file mymonmap + monmaptool: removing foo + monmaptool: writing epoch 2 to mymonmap (0 monitors) + + $ monmaptool --print mymonmap + monmaptool: monmap file mymonmap + epoch 2 + fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re) + last_changed \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re) + created \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re) + + $ NEW_FSID="$(monmaptool --print mymonmap|grep ^fsid)" + $ [ "$ORIG_FSID" = "$NEW_FSID" ] diff --git a/src/test/cli/monmaptool/simple.t b/src/test/cli/monmaptool/simple.t new file mode 100644 index 0000000000000..9acd15919d52c --- /dev/null +++ b/src/test/cli/monmaptool/simple.t @@ -0,0 +1,3 @@ + $ monmaptool + usage: [--print] [--create [--clobber]] [--add name 1.2.3.4:567] [--rm name] + [1] diff --git a/src/test/cli/osdmaptool/clobber.t b/src/test/cli/osdmaptool/clobber.t new file mode 100644 index 0000000000000..f08da2db44702 --- /dev/null +++ b/src/test/cli/osdmaptool/clobber.t @@ -0,0 +1,68 @@ +# TODO it seems osdmaptool refuses to create files at all without --clobber + $ osdmaptool --createsimple 3 myosdmap + osdmaptool: osdmap file 'myosdmap' + \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ [0-9a-f]{12} can't open myosdmap: error 2: No such file or directory (re) + osdmaptool: couldn't open myosdmap: error -2: Unknown error 18446744073709551614 + [255] + + $ osdmaptool --createsimple 3 --clobber myosdmap + osdmaptool: osdmap file 'myosdmap' + osdmaptool: writing epoch 1 to myosdmap + +# TODO it seems osdmaptool will happily overwrite without --clobber + $ osdmaptool --createsimple 3 myosdmap + osdmaptool: osdmap file 'myosdmap' + osdmaptool: writing epoch 1 to myosdmap + + $ ORIG_FSID="$(osdmaptool --print myosdmap|grep ^fsid)" + +# hasn't changed yet +#TODO typo +#TODO this one has so many pg_pools only because the above --clobber appended instead of overwriting? + $ osdmaptool --print myosdmap + osdmaptool: osdmap file 'myosdmap' + epoch 1 + fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re) + created \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re) + modifed \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re) + flags + + pg_pool 0 'data' pg_pool(rep pg_size 2 crush_ruleset 0 object_hash rjenkins pg_num 1536 pgp_num 192 lpg_num 2 lpgp_num 2 last_change 0 owner 0) + pg_pool 1 'metadata' pg_pool(rep pg_size 2 crush_ruleset 1 object_hash rjenkins pg_num 1536 pgp_num 192 lpg_num 2 lpgp_num 2 last_change 0 owner 0) + pg_pool 2 'casdata' pg_pool(rep pg_size 2 crush_ruleset 2 object_hash rjenkins pg_num 1536 pgp_num 192 lpg_num 2 lpgp_num 2 last_change 0 owner 0) + pg_pool 3 'rbd' pg_pool(rep pg_size 2 crush_ruleset 3 object_hash rjenkins pg_num 1536 pgp_num 192 lpg_num 2 lpgp_num 2 last_change 0 owner 0) + pg_pool 4 'data' pg_pool(rep pg_size 2 crush_ruleset 0 object_hash rjenkins pg_num 1536 pgp_num 192 lpg_num 2 lpgp_num 2 last_change 0 owner 0) + pg_pool 5 'metadata' pg_pool(rep pg_size 2 crush_ruleset 1 object_hash rjenkins pg_num 1536 pgp_num 192 lpg_num 2 lpgp_num 2 last_change 0 owner 0) + pg_pool 6 'casdata' pg_pool(rep pg_size 2 crush_ruleset 2 object_hash rjenkins pg_num 1536 pgp_num 192 lpg_num 2 lpgp_num 2 last_change 0 owner 0) + pg_pool 7 'rbd' pg_pool(rep pg_size 2 crush_ruleset 3 object_hash rjenkins pg_num 1536 pgp_num 192 lpg_num 2 lpgp_num 2 last_change 0 owner 0) + + max_osd 3 + + + $ NEW_FSID="$(osdmaptool --print myosdmap|grep ^fsid)" + $ [ "$ORIG_FSID" = "$NEW_FSID" ] + + $ osdmaptool --createsimple 1 --clobber myosdmap + osdmaptool: osdmap file 'myosdmap' + osdmaptool: writing epoch 1 to myosdmap + + $ osdmaptool --print myosdmap + osdmaptool: osdmap file 'myosdmap' + epoch 1 + fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re) + created \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re) + modifed \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re) + flags + + pg_pool 0 'data' pg_pool(rep pg_size 2 crush_ruleset 0 object_hash rjenkins pg_num 512 pgp_num 64 lpg_num 2 lpgp_num 2 last_change 0 owner 0) + pg_pool 1 'metadata' pg_pool(rep pg_size 2 crush_ruleset 1 object_hash rjenkins pg_num 512 pgp_num 64 lpg_num 2 lpgp_num 2 last_change 0 owner 0) + pg_pool 2 'casdata' pg_pool(rep pg_size 2 crush_ruleset 2 object_hash rjenkins pg_num 512 pgp_num 64 lpg_num 2 lpgp_num 2 last_change 0 owner 0) + pg_pool 3 'rbd' pg_pool(rep pg_size 2 crush_ruleset 3 object_hash rjenkins pg_num 512 pgp_num 64 lpg_num 2 lpgp_num 2 last_change 0 owner 0) + + max_osd 1 + + + $ NEW_FSID="$(osdmaptool --print myosdmap|grep ^fsid)" +#TODO --clobber should probably set new fsid, remove the [1] + $ [ "$ORIG_FSID" != "$NEW_FSID" ] + [1] diff --git a/src/test/cli/osdmaptool/create-print.t b/src/test/cli/osdmaptool/create-print.t new file mode 100644 index 0000000000000..1ff681e90922c --- /dev/null +++ b/src/test/cli/osdmaptool/create-print.t @@ -0,0 +1,26 @@ +# TODO it seems osdmaptool refuses to create files at all without --clobber + $ osdmaptool --createsimple 3 myosdmap + osdmaptool: osdmap file 'myosdmap' + \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ [0-9a-f]{12} can't open myosdmap: error 2: No such file or directory (re) + osdmaptool: couldn't open myosdmap: error -2: Unknown error 18446744073709551614 + [255] + + $ osdmaptool --createsimple 3 --clobber myosdmap + osdmaptool: osdmap file 'myosdmap' + osdmaptool: writing epoch 1 to myosdmap + + $ osdmaptool --print myosdmap + osdmaptool: osdmap file 'myosdmap' + epoch 1 + fsid [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (re) + created \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re) + modifed \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ (re) + flags + + pg_pool 0 'data' pg_pool(rep pg_size 2 crush_ruleset 0 object_hash rjenkins pg_num 1536 pgp_num 192 lpg_num 2 lpgp_num 2 last_change 0 owner 0) + pg_pool 1 'metadata' pg_pool(rep pg_size 2 crush_ruleset 1 object_hash rjenkins pg_num 1536 pgp_num 192 lpg_num 2 lpgp_num 2 last_change 0 owner 0) + pg_pool 2 'casdata' pg_pool(rep pg_size 2 crush_ruleset 2 object_hash rjenkins pg_num 1536 pgp_num 192 lpg_num 2 lpgp_num 2 last_change 0 owner 0) + pg_pool 3 'rbd' pg_pool(rep pg_size 2 crush_ruleset 3 object_hash rjenkins pg_num 1536 pgp_num 192 lpg_num 2 lpgp_num 2 last_change 0 owner 0) + + max_osd 3 + diff --git a/src/test/cli/osdmaptool/help.t b/src/test/cli/osdmaptool/help.t new file mode 100644 index 0000000000000..e7d424f9cd06d --- /dev/null +++ b/src/test/cli/osdmaptool/help.t @@ -0,0 +1,7 @@ +# TODO be user-friendly + $ osdmaptool --help + usage: [--print] [--createsimple [--clobber] [--pg_bits ]] + --export-crush write osdmap's crush map to + --import-crush replace osdmap's crush map with + --test-map-pg map a pgid to osds + [1] diff --git a/src/test/cli/osdmaptool/print-empty.t b/src/test/cli/osdmaptool/print-empty.t new file mode 100644 index 0000000000000..a629f7717702e --- /dev/null +++ b/src/test/cli/osdmaptool/print-empty.t @@ -0,0 +1,5 @@ + $ touch empty + $ osdmaptool --print empty + osdmaptool: osdmap file 'empty' + osdmaptool: error decoding osdmap 'empty' + [255] diff --git a/src/test/cli/osdmaptool/print-nonexistent.t b/src/test/cli/osdmaptool/print-nonexistent.t new file mode 100644 index 0000000000000..6a6411d26ac87 --- /dev/null +++ b/src/test/cli/osdmaptool/print-nonexistent.t @@ -0,0 +1,5 @@ + $ osdmaptool --print nonexistent + osdmaptool: osdmap file 'nonexistent' + \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d+ [0-9a-f]{12} can't open nonexistent: error 2: No such file or directory (re) + osdmaptool: couldn't open nonexistent: error -2: Unknown error 18446744073709551614 + [255] diff --git a/src/test/cli/osdmaptool/simple.t b/src/test/cli/osdmaptool/simple.t new file mode 100644 index 0000000000000..535bd5d174787 --- /dev/null +++ b/src/test/cli/osdmaptool/simple.t @@ -0,0 +1,7 @@ + $ osdmaptool + osdmaptool: must specify osdmap filename + usage: [--print] [--createsimple [--clobber] [--pg_bits ]] + --export-crush write osdmap's crush map to + --import-crush replace osdmap's crush map with + --test-map-pg map a pgid to osds + [1] diff --git a/src/test/downloads/cram-0.5.tar.gz b/src/test/downloads/cram-0.5.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..c82724056b4941d898823f0b602c571ad606eddc GIT binary patch literal 21815 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
  • *3u8&0Jzg*b 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 literal 0 HcmV?d00001 diff --git a/src/test/run-cli-tests b/src/test/run-cli-tests new file mode 100755 index 0000000000000..aee6fd087e8b4 --- /dev/null +++ b/src/test/run-cli-tests @@ -0,0 +1,22 @@ +#!/bin/sh +set -e + +if ! command -v pip >/dev/null; then + echo "$0: PIP not installed, skipping python-using tests." 1>&2 + exit 1 +fi + +HERE="$(dirname "$0")" +VENV="$HERE/virtualenv" +CRAM_BIN="$VENV/bin/cram" +if [ ! -e "$CRAM_BIN" ]; then + pip -E "$VENV" install downloads/cram-0.5.tar.gz +fi + +HERE_ABS="$(readlink -f "$HERE")" + +# cram doesn't like seeing the same foo.t basename twice on the same +# run, so run it once per directory +for tool in "$HERE"/cli/*; do + PATH="$HERE_ABS/..:$PATH" "$CRAM_BIN" -v "$@" -- "$tool"/*.t +done -- 2.39.5