Revision 3add7574
b/.ghci | ||
---|---|---|
1 |
:set -ihtools -ihtest |
|
1 |
:set -isrc -ihtest |
b/.gitignore | ||
---|---|---|
11 | 11 |
*.o |
12 | 12 |
*.hi |
13 | 13 |
*.hp |
14 |
*.tix |
|
15 |
*.prof |
|
16 |
*.stat |
|
17 |
.hpc/ |
|
14 | 18 |
|
15 | 19 |
# / |
16 | 20 |
/Makefile |
... | ... | |
91 | 95 |
/htest/hroller |
92 | 96 |
/htest/hscan |
93 | 97 |
/htest/hspace |
98 |
/htest/hpc-htools |
|
99 |
/htest/hpc-mon-collector |
|
100 |
/htest/test |
|
94 | 101 |
|
95 | 102 |
# tools |
96 | 103 |
/tools/kvm-ifup |
... | ... | |
112 | 119 |
/scripts/gnt-network |
113 | 120 |
/scripts/gnt-storage |
114 | 121 |
|
115 |
# htools-specific rules |
|
116 |
/htools/apidoc |
|
117 |
/htools/.hpc |
|
118 |
/htools/coverage |
|
119 |
|
|
120 |
/htools/mon-collector |
|
121 |
/htools/htools |
|
122 |
/htools/hconfd |
|
123 |
/htools/ganeti-confd |
|
124 |
/htools/rpc-test |
|
125 |
/htest/hpc-htools |
|
126 |
/htest/hpc-mon-collector |
|
127 |
/htest/test |
|
128 |
/htools/*.prof* |
|
129 |
/htools/*.stat |
|
130 |
/htools/*.tix |
|
131 |
/htest/*.prof* |
|
132 |
/htest/*.stat |
|
133 |
/htest/*.tix |
|
134 |
/.hpc/ |
|
135 |
/*.tix |
|
122 |
# haskell-specific rules |
|
123 |
/src/mon-collector |
|
124 |
/src/htools |
|
125 |
/src/hconfd |
|
126 |
/src/ganeti-confd |
|
127 |
/src/rpc-test |
|
136 | 128 |
|
137 |
/htools/Ganeti/Constants.hs |
|
138 |
/htools/Ganeti/Version.hs |
|
129 |
# automatically-built Haskell files |
|
130 |
/src/Ganeti/Constants.hs |
|
131 |
/src/Ganeti/Version.hs |
|
139 | 132 |
/htest/Test/Ganeti/TestImports.hs |
b/Makefile.am | ||
---|---|---|
55 | 55 |
.DELETE_ON_ERROR: |
56 | 56 |
|
57 | 57 |
HTOOLS_DIRS = \ |
58 |
htools \
|
|
59 |
htools/Ganeti \
|
|
60 |
htools/Ganeti/Block \
|
|
61 |
htools/Ganeti/Block/Drbd \
|
|
62 |
htools/Ganeti/Confd \
|
|
63 |
htools/Ganeti/DataCollectors \
|
|
64 |
htools/Ganeti/HTools \
|
|
65 |
htools/Ganeti/HTools/Backend \
|
|
66 |
htools/Ganeti/HTools/Program \
|
|
67 |
htools/Ganeti/Query \
|
|
58 |
src \
|
|
59 |
src/Ganeti \
|
|
60 |
src/Ganeti/Block \
|
|
61 |
src/Ganeti/Block/Drbd \
|
|
62 |
src/Ganeti/Confd \
|
|
63 |
src/Ganeti/DataCollectors \
|
|
64 |
src/Ganeti/HTools \
|
|
65 |
src/Ganeti/HTools/Backend \
|
|
66 |
src/Ganeti/HTools/Program \
|
|
67 |
src/Ganeti/Query \
|
|
68 | 68 |
htest \ |
69 | 69 |
htest/Test \ |
70 | 70 |
htest/Test/Ganeti \ |
... | ... | |
182 | 182 |
$(nodist_pkgpython_PYTHON) \ |
183 | 183 |
$(HS_ALL_PROGS) $(HS_BUILT_SRCS) \ |
184 | 184 |
$(HS_BUILT_TEST_HELPERS) \ |
185 |
htools/ganeti-confd \
|
|
186 |
.hpc/*.mix htools/*.tix htest/*.tix \
|
|
185 |
src/ganeti-confd \
|
|
186 |
.hpc/*.mix src/*.tix htest/*.tix \
|
|
187 | 187 |
doc/hs-lint.html |
188 | 188 |
|
189 | 189 |
GENERATED_FILES = \ |
... | ... | |
195 | 195 |
if WANT_HTOOLS |
196 | 196 |
HTOOLS_GENERATED_FILES += $(HS_PROGS) |
197 | 197 |
if ENABLE_CONFD |
198 |
HTOOLS_GENERATED_FILES += htools/hconfd htools/ganeti-confd
|
|
198 |
HTOOLS_GENERATED_FILES += src/hconfd src/ganeti-confd
|
|
199 | 199 |
endif |
200 | 200 |
endif |
201 | 201 |
|
... | ... | |
408 | 408 |
doc/virtual-cluster.rst \ |
409 | 409 |
doc/walkthrough.rst |
410 | 410 |
|
411 |
HS_PROGS = htools/htools htools/mon-collector
|
|
411 |
HS_PROGS = src/htools src/mon-collector
|
|
412 | 412 |
HS_BIN_ROLES = hbal hscan hspace hinfo hcheck hroller |
413 | 413 |
HS_HTOOLS_PROGS = $(HS_BIN_ROLES) hail |
414 | 414 |
|
... | ... | |
417 | 417 |
htest/hpc-htools \ |
418 | 418 |
htest/hpc-mon-collector \ |
419 | 419 |
htest/test \ |
420 |
htools/hconfd \
|
|
421 |
htools/rpc-test
|
|
420 |
src/hconfd \
|
|
421 |
src/rpc-test
|
|
422 | 422 |
|
423 | 423 |
HS_PROG_SRCS = $(patsubst %,%.hs,$(HS_ALL_PROGS)) |
424 | 424 |
HS_BUILT_TEST_HELPERS = $(HS_BIN_ROLES:%=htest/%) htest/hail |
425 | 425 |
|
426 | 426 |
HFLAGS = \ |
427 |
-O -Wall -Werror -ihtools \
|
|
427 |
-O -Wall -Werror -isrc \
|
|
428 | 428 |
-fwarn-monomorphism-restriction \ |
429 | 429 |
-fwarn-tabs \ |
430 | 430 |
$(GHC_BYVERSION_FLAGS) |
... | ... | |
444 | 444 |
--exclude Test.Ganeti.TestHTools \ |
445 | 445 |
--exclude Test.Ganeti.TestHelper \ |
446 | 446 |
--exclude Test.Ganeti.TestImports \ |
447 |
$(patsubst htools.%,--exclude Test.%,$(subst /,.,$(patsubst %.hs,%, $(HS_LIB_SRCS))))
|
|
447 |
$(patsubst src.%,--exclude Test.%,$(subst /,.,$(patsubst %.hs,%, $(HS_LIB_SRCS))))
|
|
448 | 448 |
|
449 | 449 |
HS_LIB_SRCS = \ |
450 |
htools/Ganeti/Block/Drbd/Types.hs \
|
|
451 |
htools/Ganeti/Block/Drbd/Parser.hs \
|
|
452 |
htools/Ganeti/BasicTypes.hs \
|
|
453 |
htools/Ganeti/Common.hs \
|
|
454 |
htools/Ganeti/Compat.hs \
|
|
455 |
htools/Ganeti/Confd/Client.hs \
|
|
456 |
htools/Ganeti/Confd/Server.hs \
|
|
457 |
htools/Ganeti/Confd/Types.hs \
|
|
458 |
htools/Ganeti/Confd/Utils.hs \
|
|
459 |
htools/Ganeti/Config.hs \
|
|
460 |
htools/Ganeti/Daemon.hs \
|
|
461 |
htools/Ganeti/DataCollectors/CLI.hs \
|
|
462 |
htools/Ganeti/DataCollectors/Drbd.hs \
|
|
463 |
htools/Ganeti/DataCollectors/Program.hs \
|
|
464 |
htools/Ganeti/Errors.hs \
|
|
465 |
htools/Ganeti/HTools/Backend/IAlloc.hs \
|
|
466 |
htools/Ganeti/HTools/Backend/Luxi.hs \
|
|
467 |
htools/Ganeti/HTools/Backend/Rapi.hs \
|
|
468 |
htools/Ganeti/HTools/Backend/Simu.hs \
|
|
469 |
htools/Ganeti/HTools/Backend/Text.hs \
|
|
470 |
htools/Ganeti/HTools/CLI.hs \
|
|
471 |
htools/Ganeti/HTools/Cluster.hs \
|
|
472 |
htools/Ganeti/HTools/Container.hs \
|
|
473 |
htools/Ganeti/HTools/ExtLoader.hs \
|
|
474 |
htools/Ganeti/HTools/Graph.hs \
|
|
475 |
htools/Ganeti/HTools/Group.hs \
|
|
476 |
htools/Ganeti/HTools/Instance.hs \
|
|
477 |
htools/Ganeti/HTools/Loader.hs \
|
|
478 |
htools/Ganeti/HTools/Node.hs \
|
|
479 |
htools/Ganeti/HTools/PeerMap.hs \
|
|
480 |
htools/Ganeti/HTools/Program.hs \
|
|
481 |
htools/Ganeti/HTools/Program/Hail.hs \
|
|
482 |
htools/Ganeti/HTools/Program/Hbal.hs \
|
|
483 |
htools/Ganeti/HTools/Program/Hcheck.hs \
|
|
484 |
htools/Ganeti/HTools/Program/Hinfo.hs \
|
|
485 |
htools/Ganeti/HTools/Program/Hscan.hs \
|
|
486 |
htools/Ganeti/HTools/Program/Hspace.hs \
|
|
487 |
htools/Ganeti/HTools/Program/Hroller.hs \
|
|
488 |
htools/Ganeti/HTools/Types.hs \
|
|
489 |
htools/Ganeti/Hash.hs \
|
|
490 |
htools/Ganeti/JQueue.hs \
|
|
491 |
htools/Ganeti/JSON.hs \
|
|
492 |
htools/Ganeti/Jobs.hs \
|
|
493 |
htools/Ganeti/Logging.hs \
|
|
494 |
htools/Ganeti/Luxi.hs \
|
|
495 |
htools/Ganeti/Network.hs \
|
|
496 |
htools/Ganeti/Objects.hs \
|
|
497 |
htools/Ganeti/OpCodes.hs \
|
|
498 |
htools/Ganeti/OpParams.hs \
|
|
499 |
htools/Ganeti/Path.hs \
|
|
500 |
htools/Ganeti/Query/Common.hs \
|
|
501 |
htools/Ganeti/Query/Filter.hs \
|
|
502 |
htools/Ganeti/Query/Group.hs \
|
|
503 |
htools/Ganeti/Query/Job.hs \
|
|
504 |
htools/Ganeti/Query/Language.hs \
|
|
505 |
htools/Ganeti/Query/Node.hs \
|
|
506 |
htools/Ganeti/Query/Query.hs \
|
|
507 |
htools/Ganeti/Query/Server.hs \
|
|
508 |
htools/Ganeti/Query/Types.hs \
|
|
509 |
htools/Ganeti/Rpc.hs \
|
|
510 |
htools/Ganeti/Runtime.hs \
|
|
511 |
htools/Ganeti/Ssconf.hs \
|
|
512 |
htools/Ganeti/THH.hs \
|
|
513 |
htools/Ganeti/Types.hs \
|
|
514 |
htools/Ganeti/Utils.hs
|
|
450 |
src/Ganeti/Block/Drbd/Types.hs \
|
|
451 |
src/Ganeti/Block/Drbd/Parser.hs \
|
|
452 |
src/Ganeti/BasicTypes.hs \
|
|
453 |
src/Ganeti/Common.hs \
|
|
454 |
src/Ganeti/Compat.hs \
|
|
455 |
src/Ganeti/Confd/Client.hs \
|
|
456 |
src/Ganeti/Confd/Server.hs \
|
|
457 |
src/Ganeti/Confd/Types.hs \
|
|
458 |
src/Ganeti/Confd/Utils.hs \
|
|
459 |
src/Ganeti/Config.hs \
|
|
460 |
src/Ganeti/Daemon.hs \
|
|
461 |
src/Ganeti/DataCollectors/CLI.hs \
|
|
462 |
src/Ganeti/DataCollectors/Drbd.hs \
|
|
463 |
src/Ganeti/DataCollectors/Program.hs \
|
|
464 |
src/Ganeti/Errors.hs \
|
|
465 |
src/Ganeti/HTools/Backend/IAlloc.hs \
|
|
466 |
src/Ganeti/HTools/Backend/Luxi.hs \
|
|
467 |
src/Ganeti/HTools/Backend/Rapi.hs \
|
|
468 |
src/Ganeti/HTools/Backend/Simu.hs \
|
|
469 |
src/Ganeti/HTools/Backend/Text.hs \
|
|
470 |
src/Ganeti/HTools/CLI.hs \
|
|
471 |
src/Ganeti/HTools/Cluster.hs \
|
|
472 |
src/Ganeti/HTools/Container.hs \
|
|
473 |
src/Ganeti/HTools/ExtLoader.hs \
|
|
474 |
src/Ganeti/HTools/Graph.hs \
|
|
475 |
src/Ganeti/HTools/Group.hs \
|
|
476 |
src/Ganeti/HTools/Instance.hs \
|
|
477 |
src/Ganeti/HTools/Loader.hs \
|
|
478 |
src/Ganeti/HTools/Node.hs \
|
|
479 |
src/Ganeti/HTools/PeerMap.hs \
|
|
480 |
src/Ganeti/HTools/Program.hs \
|
|
481 |
src/Ganeti/HTools/Program/Hail.hs \
|
|
482 |
src/Ganeti/HTools/Program/Hbal.hs \
|
|
483 |
src/Ganeti/HTools/Program/Hcheck.hs \
|
|
484 |
src/Ganeti/HTools/Program/Hinfo.hs \
|
|
485 |
src/Ganeti/HTools/Program/Hscan.hs \
|
|
486 |
src/Ganeti/HTools/Program/Hspace.hs \
|
|
487 |
src/Ganeti/HTools/Program/Hroller.hs \
|
|
488 |
src/Ganeti/HTools/Types.hs \
|
|
489 |
src/Ganeti/Hash.hs \
|
|
490 |
src/Ganeti/JQueue.hs \
|
|
491 |
src/Ganeti/JSON.hs \
|
|
492 |
src/Ganeti/Jobs.hs \
|
|
493 |
src/Ganeti/Logging.hs \
|
|
494 |
src/Ganeti/Luxi.hs \
|
|
495 |
src/Ganeti/Network.hs \
|
|
496 |
src/Ganeti/Objects.hs \
|
|
497 |
src/Ganeti/OpCodes.hs \
|
|
498 |
src/Ganeti/OpParams.hs \
|
|
499 |
src/Ganeti/Path.hs \
|
|
500 |
src/Ganeti/Query/Common.hs \
|
|
501 |
src/Ganeti/Query/Filter.hs \
|
|
502 |
src/Ganeti/Query/Group.hs \
|
|
503 |
src/Ganeti/Query/Job.hs \
|
|
504 |
src/Ganeti/Query/Language.hs \
|
|
505 |
src/Ganeti/Query/Node.hs \
|
|
506 |
src/Ganeti/Query/Query.hs \
|
|
507 |
src/Ganeti/Query/Server.hs \
|
|
508 |
src/Ganeti/Query/Types.hs \
|
|
509 |
src/Ganeti/Rpc.hs \
|
|
510 |
src/Ganeti/Runtime.hs \
|
|
511 |
src/Ganeti/Ssconf.hs \
|
|
512 |
src/Ganeti/THH.hs \
|
|
513 |
src/Ganeti/Types.hs \
|
|
514 |
src/Ganeti/Utils.hs
|
|
515 | 515 |
|
516 | 516 |
HS_TEST_SRCS = \ |
517 | 517 |
htest/Test/Ganeti/Attoparsec.hs \ |
... | ... | |
557 | 557 |
|
558 | 558 |
HS_BUILT_SRCS = \ |
559 | 559 |
htest/Test/Ganeti/TestImports.hs \ |
560 |
htools/Ganeti/Constants.hs \
|
|
561 |
htools/Ganeti/Version.hs
|
|
560 |
src/Ganeti/Constants.hs \
|
|
561 |
src/Ganeti/Version.hs
|
|
562 | 562 |
HS_BUILT_SRCS_IN = $(patsubst %,%.in,$(HS_BUILT_SRCS)) |
563 | 563 |
|
564 | 564 |
$(RUN_IN_TEMPDIR): | stamp-directories |
... | ... | |
658 | 658 |
|
659 | 659 |
bin_SCRIPTS = |
660 | 660 |
if WANT_HTOOLS |
661 |
bin_SCRIPTS += $(filter-out htools/hail,$(HS_PROGS))
|
|
661 |
bin_SCRIPTS += $(filter-out src/hail,$(HS_PROGS))
|
|
662 | 662 |
install-exec-hook: |
663 | 663 |
@mkdir_p@ $(DESTDIR)$(iallocatorsdir) |
664 | 664 |
# FIXME: this is a hardcoded logic, instead of auto-resolving |
... | ... | |
725 | 725 |
daemons/ganeti-cleaner |
726 | 726 |
|
727 | 727 |
if ENABLE_CONFD |
728 |
htools/ganeti-confd: htools/hconfd
|
|
728 |
src/ganeti-confd: src/hconfd
|
|
729 | 729 |
cp -f $< $@ |
730 | 730 |
|
731 |
nodist_sbin_SCRIPTS += htools/ganeti-confd
|
|
731 |
nodist_sbin_SCRIPTS += src/ganeti-confd
|
|
732 | 732 |
endif |
733 | 733 |
|
734 | 734 |
python_scripts = \ |
... | ... | |
770 | 770 |
daemons/daemon-util \ |
771 | 771 |
tools/kvm-ifup \ |
772 | 772 |
$(pkglib_python_scripts) \ |
773 |
htools/mon-collector
|
|
773 |
src/mon-collector
|
|
774 | 774 |
|
775 | 775 |
nodist_myexeclib_SCRIPTS = \ |
776 | 776 |
$(nodist_pkglib_python_scripts) |
... | ... | |
826 | 826 |
$(qa_scripts) \ |
827 | 827 |
$(HS_LIBTEST_SRCS) $(HS_BUILT_SRCS_IN) \ |
828 | 828 |
$(HS_PROG_SRCS) \ |
829 |
htools/lint-hints.hs \
|
|
829 |
src/lint-hints.hs \
|
|
830 | 830 |
htest/cli-tests-defs.sh \ |
831 | 831 |
htest/offline-test.sh \ |
832 | 832 |
.ghci |
... | ... | |
1252 | 1252 |
fi; \ |
1253 | 1253 |
fi |
1254 | 1254 |
|
1255 |
htools/Ganeti/Version.hs: htools/Ganeti/Version.hs.in \
|
|
1255 |
src/Ganeti/Version.hs: src/Ganeti/Version.hs.in \
|
|
1256 | 1256 |
vcs-version $(built_base_sources) |
1257 | 1257 |
set -e; \ |
1258 | 1258 |
VCSVER=`cat $(abs_top_srcdir)/vcs-version`; \ |
1259 | 1259 |
sed -e "s/%ver%/$$VCSVER/" < $< > $@ |
1260 | 1260 |
|
1261 |
htools/Ganeti/Constants.hs: htools/Ganeti/Constants.hs.in \
|
|
1261 |
src/Ganeti/Constants.hs: src/Ganeti/Constants.hs.in \
|
|
1262 | 1262 |
lib/constants.py lib/_autoconf.py lib/luxi.py lib/errors.py \ |
1263 | 1263 |
lib/jstore.py $(RUN_IN_TEMPDIR)\ |
1264 | 1264 |
$(CONVERT_CONSTANTS) $(built_base_sources) \ |
... | ... | |
1273 | 1273 |
set -e; \ |
1274 | 1274 |
{ cat $< ; \ |
1275 | 1275 |
echo ; \ |
1276 |
for name in $(filter-out Ganeti.THH,$(subst /,.,$(patsubst %.hs,%,$(patsubst htools/%,%,$(HS_LIB_SRCS))))) ; do \
|
|
1276 |
for name in $(filter-out Ganeti.THH,$(subst /,.,$(patsubst %.hs,%,$(patsubst src/%,%,$(HS_LIB_SRCS))))) ; do \
|
|
1277 | 1277 |
echo "import $$name ()" ; \ |
1278 | 1278 |
done ; \ |
1279 | 1279 |
} > $@ |
... | ... | |
1604 | 1604 |
--repeat $(pep8_python_code) |
1605 | 1605 |
|
1606 | 1606 |
# FIXME: remove ignore "Use void" when GHC 6.x is deprecated |
1607 |
HLINT_EXCLUDES = htools/Ganeti/THH.hs htest/hpc-htools.hs
|
|
1607 |
HLINT_EXCLUDES = src/Ganeti/THH.hs htest/hpc-htools.hs
|
|
1608 | 1608 |
.PHONY: hlint |
1609 |
hlint: $(HS_BUILT_SRCS) htools/lint-hints.hs
|
|
1609 |
hlint: $(HS_BUILT_SRCS) src/lint-hints.hs
|
|
1610 | 1610 |
@test -n "$(HLINT)" || { echo 'hlint' not found during configure; exit 1; } |
1611 | 1611 |
if tty -s; then C="-c"; else C=""; fi; \ |
1612 | 1612 |
$(HLINT) --utf8 --report=doc/hs-lint.html --cross $$C \ |
... | ... | |
1614 | 1614 |
--ignore "Use &&&" \ |
1615 | 1615 |
--ignore "Use void" \ |
1616 | 1616 |
--ignore "Reduce duplication" \ |
1617 |
--hint htools/lint-hints \
|
|
1617 |
--hint src/lint-hints \
|
|
1618 | 1618 |
$(filter-out $(HLINT_EXCLUDES),$(HS_LIBTEST_SRCS) $(HS_PROG_SRCS)) |
1619 | 1619 |
|
1620 | 1620 |
# a dist hook rule for updating the vcs-version file; this is |
... | ... | |
1711 | 1711 |
$(LN_S) ../hscolour.css $(APIDOC_HS_DIR)/Ganeti/HTools/hscolour.css |
1712 | 1712 |
$(LN_S) ../hscolour.css $(APIDOC_HS_DIR)/Ganeti/Confd/hscolour.css |
1713 | 1713 |
set -e ; \ |
1714 |
cd htools; \
|
|
1714 |
cd src; \
|
|
1715 | 1715 |
if [ "$(HTOOLS_NOCURL)" ]; \ |
1716 | 1716 |
then OPTGHC="--optghc=$(HTOOLS_NOCURL)"; \ |
1717 | 1717 |
else OPTGHC=""; \ |
... | ... | |
1722 | 1722 |
if [ "$(HTOOLS_REGEX_PCRE)" ]; \ |
1723 | 1723 |
then OPTGHC="$$OPTGHC --optghc=$(HTOOLS_REGEX_PCRE)"; \ |
1724 | 1724 |
fi; \ |
1725 |
RELSRCS="$(HS_LIB_SRCS:htools/%=%) $(patsubst htools/%,%,$(filter htools/%,$(HS_BUILT_SRCS)))"; \
|
|
1725 |
RELSRCS="$(HS_LIB_SRCS:src/%=%) $(patsubst src/%,%,$(filter src/%,$(HS_BUILT_SRCS)))"; \
|
|
1726 | 1726 |
for file in $$RELSRCS; do \ |
1727 | 1727 |
hfile=`echo $$file|sed 's/\\.hs$$//'`.html; \ |
1728 | 1728 |
$(HSCOLOUR) -css -anchor $$file > ../$(APIDOC_HS_DIR)/$$hfile ; \ |
... | ... | |
1732 | 1732 |
--source-module="%{MODULE/.//}.html" \ |
1733 | 1733 |
--source-entity="%{MODULE/.//}.html#%{NAME}" \ |
1734 | 1734 |
$$OPTGHC \ |
1735 |
$(filter-out Ganeti/HTools/ExtLoader.hs,$(HS_LIB_SRCS:htools/%=%))
|
|
1735 |
$(filter-out Ganeti/HTools/ExtLoader.hs,$(HS_LIB_SRCS:src/%=%))
|
|
1736 | 1736 |
|
1737 | 1737 |
.PHONY: TAGS |
1738 | 1738 |
TAGS: $(GENERATED_FILES) |
... | ... | |
1781 | 1781 |
.PHONY: live-test |
1782 | 1782 |
live-test: all |
1783 | 1783 |
set -e ; \ |
1784 |
cd htools; \
|
|
1784 |
cd src; \
|
|
1785 | 1785 |
rm -f .hpc; $(LN_S) ../.hpc .hpc; \ |
1786 | 1786 |
rm -f *.tix *.mix; \ |
1787 | 1787 |
./live-test.sh; \ |
1788 |
hpc sum --union $(HPCEXCL) $(addsuffix .tix,$(HS_PROGS:htools/%=%)) \
|
|
1788 |
hpc sum --union $(HPCEXCL) $(addsuffix .tix,$(HS_PROGS:src/%=%)) \
|
|
1789 | 1789 |
--output=live-test.tix ; \ |
1790 | 1790 |
@mkdir_p@ ../$(COVERAGE_HS_DIR) ; \ |
1791 | 1791 |
hpc markup --destdir=../$(COVERAGE_HS_DIR) live-test \ |
b/autotools/build-bash-completion | ||
---|---|---|
759 | 759 |
|
760 | 760 |
""" |
761 | 761 |
if htools: |
762 |
cmd = "./htools/htools"
|
|
762 |
cmd = "./src/htools"
|
|
763 | 763 |
env = {"HTOOLS": script} |
764 | 764 |
script_name = script |
765 | 765 |
func_name = "htools_%s" % script |
... | ... | |
854 | 854 |
|
855 | 855 |
# ganeti-confd, if enabled |
856 | 856 |
if _autoconf.ENABLE_CONFD: |
857 |
WriteHaskellCompletion(sw, "htools/ganeti-confd", htools=False,
|
|
857 |
WriteHaskellCompletion(sw, "src/ganeti-confd", htools=False,
|
|
858 | 858 |
debug=debug) |
859 | 859 |
|
860 | 860 |
# mon-collector, if monitoring is enabled |
861 | 861 |
if _autoconf.ENABLE_MONITORING: |
862 |
WriteHaskellCmdCompletion(sw, "htools/mon-collector", debug=debug)
|
|
862 |
WriteHaskellCmdCompletion(sw, "src/mon-collector", debug=debug)
|
|
863 | 863 |
|
864 | 864 |
# Reset extglob to original value |
865 | 865 |
sw.Write("[[ -n \"$gnt_shopt_extglob\" ]] && $gnt_shopt_extglob") |
b/autotools/run-in-tempdir | ||
---|---|---|
16 | 16 |
mv $tmpdir/lib $tmpdir/ganeti |
17 | 17 |
ln -T -s $tmpdir/ganeti $tmpdir/lib |
18 | 18 |
|
19 |
mkdir -p $tmpdir/htools $tmpdir/htest
|
|
19 |
mkdir -p $tmpdir/src $tmpdir/htest
|
|
20 | 20 |
for hfile in htools ganeti-confd mon-collector; do |
21 |
if [ -e htools/$hfile ]; then
|
|
22 |
ln -s $PWD/htools/$hfile $tmpdir/htools/
|
|
21 |
if [ -e src/$hfile ]; then
|
|
22 |
ln -s $PWD/src/$hfile $tmpdir/src/
|
|
23 | 23 |
fi |
24 | 24 |
done |
25 | 25 |
|
b/doc/devnotes.rst | ||
---|---|---|
134 | 134 |
|
135 | 135 |
$ ghci |
136 | 136 |
λ> :set -ddump-splices |
137 |
λ> :l htools/Ganeti/Objects.hs
|
|
137 |
λ> :l src/Ganeti/Objects.hs
|
|
138 | 138 |
|
139 | 139 |
And you will get the spliced code as the module is loaded. |
140 | 140 |
|
... | ... | |
150 | 150 |
or alternatively the manual sequence is:: |
151 | 151 |
|
152 | 152 |
$ make clean |
153 |
$ make htools/htools HEXTRA="-osuf .o"
|
|
154 |
$ rm htools/htools
|
|
155 |
$ make htools/htools HEXTRA="-osuf .prof_o -prof -auto-all"
|
|
153 |
$ make src/htools HEXTRA="-osuf .o"
|
|
154 |
$ rm src/htools
|
|
155 |
$ make src/htools HEXTRA="-osuf .prof_o -prof -auto-all"
|
|
156 | 156 |
|
157 | 157 |
This will build the binary twice, per the TemplateHaskell |
158 | 158 |
documentation, the second one with profiling enabled. |
b/htest/hpc-htools.hs | ||
---|---|---|
1 |
../htools/htools.hs |
|
1 |
../src/htools.hs |
b/htest/hpc-mon-collector.hs | ||
---|---|---|
1 |
../htools/mon-collector.hs |
|
1 |
../src/mon-collector.hs |
/dev/null | ||
---|---|---|
1 |
{-# LANGUAGE FlexibleInstances #-} |
|
2 |
|
|
3 |
{- |
|
4 |
|
|
5 |
Copyright (C) 2009, 2010, 2011, 2012 Google Inc. |
|
6 |
|
|
7 |
This program is free software; you can redistribute it and/or modify |
|
8 |
it under the terms of the GNU General Public License as published by |
|
9 |
the Free Software Foundation; either version 2 of the License, or |
|
10 |
(at your option) any later version. |
|
11 |
|
|
12 |
This program is distributed in the hope that it will be useful, but |
|
13 |
WITHOUT ANY WARRANTY; without even the implied warranty of |
|
14 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
15 |
General Public License for more details. |
|
16 |
|
|
17 |
You should have received a copy of the GNU General Public License |
|
18 |
along with this program; if not, write to the Free Software |
|
19 |
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
|
20 |
02110-1301, USA. |
|
21 |
|
|
22 |
-} |
|
23 |
|
|
24 |
module Ganeti.BasicTypes |
|
25 |
( GenericResult(..) |
|
26 |
, Result |
|
27 |
, ResultT(..) |
|
28 |
, resultT |
|
29 |
, FromString(..) |
|
30 |
, isOk |
|
31 |
, isBad |
|
32 |
, eitherToResult |
|
33 |
, annotateResult |
|
34 |
, select |
|
35 |
, LookupResult(..) |
|
36 |
, MatchPriority(..) |
|
37 |
, lookupName |
|
38 |
, goodLookupResult |
|
39 |
, goodMatchPriority |
|
40 |
, prefixMatch |
|
41 |
, compareNameComponent |
|
42 |
) where |
|
43 |
|
|
44 |
import Control.Applicative |
|
45 |
import Control.Monad |
|
46 |
import Control.Monad.Trans |
|
47 |
import Data.Function |
|
48 |
import Data.List |
|
49 |
|
|
50 |
-- | Generic monad for our error handling mechanisms. |
|
51 |
data GenericResult a b |
|
52 |
= Bad a |
|
53 |
| Ok b |
|
54 |
deriving (Show, Eq) |
|
55 |
|
|
56 |
-- | Type alias for a string Result. |
|
57 |
type Result = GenericResult String |
|
58 |
|
|
59 |
-- | Type class for things that can be built from strings. |
|
60 |
class FromString a where |
|
61 |
mkFromString :: String -> a |
|
62 |
|
|
63 |
-- | Trivial 'String' instance; requires FlexibleInstances extension |
|
64 |
-- though. |
|
65 |
instance FromString [Char] where |
|
66 |
mkFromString = id |
|
67 |
|
|
68 |
-- | 'Monad' instance for 'GenericResult'. |
|
69 |
instance (FromString a) => Monad (GenericResult a) where |
|
70 |
(>>=) (Bad x) _ = Bad x |
|
71 |
(>>=) (Ok x) fn = fn x |
|
72 |
return = Ok |
|
73 |
fail = Bad . mkFromString |
|
74 |
|
|
75 |
instance Functor (GenericResult a) where |
|
76 |
fmap _ (Bad msg) = Bad msg |
|
77 |
fmap fn (Ok val) = Ok (fn val) |
|
78 |
|
|
79 |
instance MonadPlus (GenericResult String) where |
|
80 |
mzero = Bad "zero Result when used as MonadPlus" |
|
81 |
-- for mplus, when we 'add' two Bad values, we concatenate their |
|
82 |
-- error descriptions |
|
83 |
(Bad x) `mplus` (Bad y) = Bad (x ++ "; " ++ y) |
|
84 |
(Bad _) `mplus` x = x |
|
85 |
x@(Ok _) `mplus` _ = x |
|
86 |
|
|
87 |
instance Applicative (GenericResult a) where |
|
88 |
pure = Ok |
|
89 |
(Bad f) <*> _ = Bad f |
|
90 |
_ <*> (Bad x) = Bad x |
|
91 |
(Ok f) <*> (Ok x) = Ok $ f x |
|
92 |
|
|
93 |
-- | This is a monad transformation for Result. It's implementation is |
|
94 |
-- based on the implementations of MaybeT and ErrorT. |
|
95 |
newtype ResultT a m b = ResultT {runResultT :: m (GenericResult a b)} |
|
96 |
|
|
97 |
instance (Monad m, FromString a) => Monad (ResultT a m) where |
|
98 |
fail err = ResultT (return . Bad $ mkFromString err) |
|
99 |
return = lift . return |
|
100 |
x >>= f = ResultT $ do |
|
101 |
a <- runResultT x |
|
102 |
case a of |
|
103 |
Ok val -> runResultT $ f val |
|
104 |
Bad err -> return $ Bad err |
|
105 |
|
|
106 |
instance MonadTrans (ResultT a) where |
|
107 |
lift x = ResultT (liftM Ok x) |
|
108 |
|
|
109 |
instance (MonadIO m, FromString a) => MonadIO (ResultT a m) where |
|
110 |
liftIO = lift . liftIO |
|
111 |
|
|
112 |
-- | Lift a `Result` value to a `ResultT`. |
|
113 |
resultT :: Monad m => GenericResult a b -> ResultT a m b |
|
114 |
resultT = ResultT . return |
|
115 |
|
|
116 |
-- | Simple checker for whether a 'GenericResult' is OK. |
|
117 |
isOk :: GenericResult a b -> Bool |
|
118 |
isOk (Ok _) = True |
|
119 |
isOk _ = False |
|
120 |
|
|
121 |
-- | Simple checker for whether a 'GenericResult' is a failure. |
|
122 |
isBad :: GenericResult a b -> Bool |
|
123 |
isBad = not . isOk |
|
124 |
|
|
125 |
-- | Converter from Either to 'GenericResult'. |
|
126 |
eitherToResult :: Either a b -> GenericResult a b |
|
127 |
eitherToResult (Left s) = Bad s |
|
128 |
eitherToResult (Right v) = Ok v |
|
129 |
|
|
130 |
-- | Annotate a Result with an ownership information. |
|
131 |
annotateResult :: String -> Result a -> Result a |
|
132 |
annotateResult owner (Bad s) = Bad $ owner ++ ": " ++ s |
|
133 |
annotateResult _ v = v |
|
134 |
|
|
135 |
-- * Misc functionality |
|
136 |
|
|
137 |
-- | Return the first result with a True condition, or the default otherwise. |
|
138 |
select :: a -- ^ default result |
|
139 |
-> [(Bool, a)] -- ^ list of \"condition, result\" |
|
140 |
-> a -- ^ first result which has a True condition, or default |
|
141 |
select def = maybe def snd . find fst |
|
142 |
|
|
143 |
-- * Lookup of partial names functionality |
|
144 |
|
|
145 |
-- | The priority of a match in a lookup result. |
|
146 |
data MatchPriority = ExactMatch |
|
147 |
| MultipleMatch |
|
148 |
| PartialMatch |
|
149 |
| FailMatch |
|
150 |
deriving (Show, Enum, Eq, Ord) |
|
151 |
|
|
152 |
-- | The result of a name lookup in a list. |
|
153 |
data LookupResult = LookupResult |
|
154 |
{ lrMatchPriority :: MatchPriority -- ^ The result type |
|
155 |
-- | Matching value (for ExactMatch, PartialMatch), Lookup string otherwise |
|
156 |
, lrContent :: String |
|
157 |
} deriving (Show) |
|
158 |
|
|
159 |
-- | Lookup results have an absolute preference ordering. |
|
160 |
instance Eq LookupResult where |
|
161 |
(==) = (==) `on` lrMatchPriority |
|
162 |
|
|
163 |
instance Ord LookupResult where |
|
164 |
compare = compare `on` lrMatchPriority |
|
165 |
|
|
166 |
-- | Check for prefix matches in names. |
|
167 |
-- Implemented in Ganeti core utils.text.MatchNameComponent |
|
168 |
-- as the regexp r"^%s(\..*)?$" % re.escape(key) |
|
169 |
prefixMatch :: String -- ^ Lookup |
|
170 |
-> String -- ^ Full name |
|
171 |
-> Bool -- ^ Whether there is a prefix match |
|
172 |
prefixMatch = isPrefixOf . (++ ".") |
|
173 |
|
|
174 |
-- | Is the lookup priority a "good" one? |
|
175 |
goodMatchPriority :: MatchPriority -> Bool |
|
176 |
goodMatchPriority ExactMatch = True |
|
177 |
goodMatchPriority PartialMatch = True |
|
178 |
goodMatchPriority _ = False |
|
179 |
|
|
180 |
-- | Is the lookup result an actual match? |
|
181 |
goodLookupResult :: LookupResult -> Bool |
|
182 |
goodLookupResult = goodMatchPriority . lrMatchPriority |
|
183 |
|
|
184 |
-- | Compares a canonical name and a lookup string. |
|
185 |
compareNameComponent :: String -- ^ Canonical (target) name |
|
186 |
-> String -- ^ Partial (lookup) name |
|
187 |
-> LookupResult -- ^ Result of the lookup |
|
188 |
compareNameComponent cnl lkp = |
|
189 |
select (LookupResult FailMatch lkp) |
|
190 |
[ (cnl == lkp , LookupResult ExactMatch cnl) |
|
191 |
, (prefixMatch lkp cnl , LookupResult PartialMatch cnl) |
|
192 |
] |
|
193 |
|
|
194 |
-- | Lookup a string and choose the best result. |
|
195 |
chooseLookupResult :: String -- ^ Lookup key |
|
196 |
-> String -- ^ String to compare to the lookup key |
|
197 |
-> LookupResult -- ^ Previous result |
|
198 |
-> LookupResult -- ^ New result |
|
199 |
chooseLookupResult lkp cstr old = |
|
200 |
-- default: use class order to pick the minimum result |
|
201 |
select (min new old) |
|
202 |
-- special cases: |
|
203 |
-- short circuit if the new result is an exact match |
|
204 |
[ (lrMatchPriority new == ExactMatch, new) |
|
205 |
-- if both are partial matches generate a multiple match |
|
206 |
, (partial2, LookupResult MultipleMatch lkp) |
|
207 |
] where new = compareNameComponent cstr lkp |
|
208 |
partial2 = all ((PartialMatch==) . lrMatchPriority) [old, new] |
|
209 |
|
|
210 |
-- | Find the canonical name for a lookup string in a list of names. |
|
211 |
lookupName :: [String] -- ^ List of keys |
|
212 |
-> String -- ^ Lookup string |
|
213 |
-> LookupResult -- ^ Result of the lookup |
|
214 |
lookupName l s = foldr (chooseLookupResult s) |
|
215 |
(LookupResult FailMatch s) l |
/dev/null | ||
---|---|---|
1 |
{-# LANGUAGE OverloadedStrings #-} |
|
2 |
{-| DRBD proc file parser |
|
3 |
|
|
4 |
This module holds the definition of the parser that extracts status |
|
5 |
information from the DRBD proc file. |
|
6 |
|
|
7 |
-} |
|
8 |
{- |
|
9 |
|
|
10 |
Copyright (C) 2012 Google Inc. |
|
11 |
|
|
12 |
This program is free software; you can redistribute it and/or modify |
|
13 |
it under the terms of the GNU General Public License as published by |
|
14 |
the Free Software Foundation; either version 2 of the License, or |
|
15 |
(at your option) any later version. |
|
16 |
|
|
17 |
This program is distributed in the hope that it will be useful, but |
|
18 |
WITHOUT ANY WARRANTY; without even the implied warranty of |
|
19 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
20 |
General Public License for more details. |
|
21 |
|
|
22 |
You should have received a copy of the GNU General Public License |
|
23 |
along with this program; if not, write to the Free Software |
|
24 |
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
|
25 |
02110-1301, USA. |
|
26 |
|
|
27 |
-} |
|
28 |
module Ganeti.Block.Drbd.Parser (drbdStatusParser, commaIntParser) where |
|
29 |
|
|
30 |
import Control.Applicative ((<*>), (*>), (<*), (<$>), (<|>), pure) |
|
31 |
import qualified Data.Attoparsec.Text as A |
|
32 |
import qualified Data.Attoparsec.Combinator as AC |
|
33 |
import Data.Attoparsec.Text (Parser) |
|
34 |
import Data.Maybe |
|
35 |
import Data.Text (Text, unpack) |
|
36 |
|
|
37 |
import Ganeti.Block.Drbd.Types |
|
38 |
|
|
39 |
-- | Our own space-skipping function, because A.skipSpace also skips |
|
40 |
-- newline characters. It skips ZERO or more spaces, so it does not |
|
41 |
-- fail if there are no spaces. |
|
42 |
skipSpaces :: Parser () |
|
43 |
skipSpaces = A.skipWhile A.isHorizontalSpace |
|
44 |
|
|
45 |
-- | Skips spaces and the given string, then executes a parser and |
|
46 |
-- returns its result. |
|
47 |
skipSpacesAndString :: Text -> Parser a -> Parser a |
|
48 |
skipSpacesAndString s parser = |
|
49 |
skipSpaces |
|
50 |
*> A.string s |
|
51 |
*> parser |
|
52 |
|
|
53 |
-- | Predicate verifying (potentially bad) end of lines |
|
54 |
isBadEndOfLine :: Char -> Bool |
|
55 |
isBadEndOfLine c = (c == '\0') || A.isEndOfLine c |
|
56 |
|
|
57 |
-- | Takes a parser and returns it with the content wrapped in a Maybe |
|
58 |
-- object. The resulting parser never fails, but contains Nothing if |
|
59 |
-- it couldn't properly parse the string. |
|
60 |
optional :: Parser a -> Parser (Maybe a) |
|
61 |
optional parser = (Just <$> parser) <|> pure Nothing |
|
62 |
|
|
63 |
-- | The parser for a whole DRBD status file. |
|
64 |
drbdStatusParser :: Parser DRBDStatus |
|
65 |
drbdStatusParser = |
|
66 |
DRBDStatus <$> versionInfoParser |
|
67 |
<*> deviceParser `AC.manyTill` A.endOfInput |
|
68 |
<* A.endOfInput |
|
69 |
|
|
70 |
-- | The parser for the version information lines. |
|
71 |
versionInfoParser :: Parser VersionInfo |
|
72 |
versionInfoParser = do |
|
73 |
versionF <- optional versionP |
|
74 |
apiF <- optional apiP |
|
75 |
protoF <- optional protoP |
|
76 |
srcVersionF <- optional srcVersion |
|
77 |
ghF <- fmap unpack <$> optional gh |
|
78 |
builderF <- fmap unpack <$> optional builder |
|
79 |
if isNothing versionF |
|
80 |
&& isNothing apiF |
|
81 |
&& isNothing protoF |
|
82 |
&& isNothing srcVersionF |
|
83 |
&& isNothing ghF |
|
84 |
&& isNothing builderF |
|
85 |
then fail "versionInfo" |
|
86 |
else pure $ VersionInfo versionF apiF protoF srcVersionF ghF builderF |
|
87 |
|
|
88 |
where versionP = |
|
89 |
A.string "version:" |
|
90 |
*> skipSpaces |
|
91 |
*> fmap unpack (A.takeWhile $ not . A.isHorizontalSpace) |
|
92 |
apiP = |
|
93 |
skipSpacesAndString "(api:" . fmap unpack $ A.takeWhile (/= '/') |
|
94 |
protoP = |
|
95 |
A.string "/proto:" |
|
96 |
*> fmap Data.Text.unpack (A.takeWhile (/= ')')) |
|
97 |
<* A.takeTill A.isEndOfLine <* A.endOfLine |
|
98 |
srcVersion = |
|
99 |
A.string "srcversion:" |
|
100 |
*> AC.skipMany1 A.space |
|
101 |
*> fmap unpack (A.takeTill A.isEndOfLine) |
|
102 |
<* A.endOfLine |
|
103 |
gh = |
|
104 |
A.string "GIT-hash:" |
|
105 |
*> skipSpaces |
|
106 |
*> A.takeWhile (not . A.isHorizontalSpace) |
|
107 |
builder = |
|
108 |
skipSpacesAndString "build by" $ |
|
109 |
skipSpaces |
|
110 |
*> A.takeTill A.isEndOfLine |
|
111 |
<* A.endOfLine |
|
112 |
|
|
113 |
-- | The parser for a (multi-line) string representing a device. |
|
114 |
deviceParser :: Parser DeviceInfo |
|
115 |
deviceParser = do |
|
116 |
deviceNum <- skipSpaces *> A.decimal <* A.char ':' |
|
117 |
cs <- skipSpacesAndString "cs:" connStateParser |
|
118 |
if cs == Unconfigured |
|
119 |
then do |
|
120 |
_ <- additionalEOL |
|
121 |
return $ UnconfiguredDevice deviceNum |
|
122 |
else do |
|
123 |
ro <- skipSpaces *> skipRoleString *> localRemoteParser roleParser |
|
124 |
ds <- skipSpacesAndString "ds:" $ localRemoteParser diskStateParser |
|
125 |
replicProtocol <- A.space *> A.anyChar |
|
126 |
io <- skipSpaces *> ioFlagsParser <* A.skipWhile isBadEndOfLine |
|
127 |
pIndicators <- perfIndicatorsParser |
|
128 |
syncS <- conditionalSyncStatusParser cs |
|
129 |
reS <- optional resyncParser |
|
130 |
act <- optional actLogParser |
|
131 |
_ <- additionalEOL |
|
132 |
return $ DeviceInfo deviceNum cs ro ds replicProtocol io pIndicators |
|
133 |
syncS reS act |
|
134 |
|
|
135 |
where conditionalSyncStatusParser SyncSource = Just <$> syncStatusParser |
|
136 |
conditionalSyncStatusParser SyncTarget = Just <$> syncStatusParser |
|
137 |
conditionalSyncStatusParser _ = pure Nothing |
|
138 |
skipRoleString = A.string "ro:" <|> A.string "st:" |
|
139 |
resyncParser = skipSpacesAndString "resync:" additionalInfoParser |
|
140 |
actLogParser = skipSpacesAndString "act_log:" additionalInfoParser |
|
141 |
additionalEOL = A.skipWhile A.isEndOfLine |
|
142 |
|
|
143 |
-- | The parser for the connection state. |
|
144 |
connStateParser :: Parser ConnState |
|
145 |
connStateParser = |
|
146 |
standAlone |
|
147 |
<|> disconnecting |
|
148 |
<|> unconnected |
|
149 |
<|> timeout |
|
150 |
<|> brokenPipe |
|
151 |
<|> networkFailure |
|
152 |
<|> protocolError |
|
153 |
<|> tearDown |
|
154 |
<|> wfConnection |
|
155 |
<|> wfReportParams |
|
156 |
<|> connected |
|
157 |
<|> startingSyncS |
|
158 |
<|> startingSyncT |
|
159 |
<|> wfBitMapS |
|
160 |
<|> wfBitMapT |
|
161 |
<|> wfSyncUUID |
|
162 |
<|> syncSource |
|
163 |
<|> syncTarget |
|
164 |
<|> pausedSyncS |
|
165 |
<|> pausedSyncT |
|
166 |
<|> verifyS |
|
167 |
<|> verifyT |
|
168 |
<|> unconfigured |
|
169 |
where standAlone = A.string "StandAlone" *> pure StandAlone |
|
170 |
disconnecting = A.string "Disconnectiog" *> pure Disconnecting |
|
171 |
unconnected = A.string "Unconnected" *> pure Unconnected |
|
172 |
timeout = A.string "Timeout" *> pure Timeout |
|
173 |
brokenPipe = A.string "BrokenPipe" *> pure BrokenPipe |
|
174 |
networkFailure = A.string "NetworkFailure" *> pure NetworkFailure |
|
175 |
protocolError = A.string "ProtocolError" *> pure ProtocolError |
|
176 |
tearDown = A.string "TearDown" *> pure TearDown |
|
177 |
wfConnection = A.string "WFConnection" *> pure WFConnection |
|
178 |
wfReportParams = A.string "WFReportParams" *> pure WFReportParams |
|
179 |
connected = A.string "Connected" *> pure Connected |
|
180 |
startingSyncS = A.string "StartingSyncS" *> pure StartingSyncS |
|
181 |
startingSyncT = A.string "StartingSyncT" *> pure StartingSyncT |
|
182 |
wfBitMapS = A.string "WFBitMapS" *> pure WFBitMapS |
|
183 |
wfBitMapT = A.string "WFBitMapT" *> pure WFBitMapT |
|
184 |
wfSyncUUID = A.string "WFSyncUUID" *> pure WFSyncUUID |
|
185 |
syncSource = A.string "SyncSource" *> pure SyncSource |
|
186 |
syncTarget = A.string "SyncTarget" *> pure SyncTarget |
|
187 |
pausedSyncS = A.string "PausedSyncS" *> pure PausedSyncS |
|
188 |
pausedSyncT = A.string "PausedSyncT" *> pure PausedSyncT |
|
189 |
verifyS = A.string "VerifyS" *> pure VerifyS |
|
190 |
verifyT = A.string "VerifyT" *> pure VerifyT |
|
191 |
unconfigured = A.string "Unconfigured" *> pure Unconfigured |
|
192 |
|
|
193 |
-- | Parser for recognizing strings describing two elements of the |
|
194 |
-- same type separated by a '/'. The first one is considered local, |
|
195 |
-- the second remote. |
|
196 |
localRemoteParser :: Parser a -> Parser (LocalRemote a) |
|
197 |
localRemoteParser parser = LocalRemote <$> parser <*> (A.char '/' *> parser) |
|
198 |
|
|
199 |
-- | The parser for resource roles. |
|
200 |
roleParser :: Parser Role |
|
201 |
roleParser = |
|
202 |
primary |
|
203 |
<|> secondary |
|
204 |
<|> unknown |
|
205 |
where primary = A.string "Primary" *> pure Primary |
|
206 |
secondary = A.string "Secondary" *> pure Secondary |
|
207 |
unknown = A.string "Unknown" *> pure Unknown |
|
208 |
|
|
209 |
-- | The parser for disk states. |
|
210 |
diskStateParser :: Parser DiskState |
|
211 |
diskStateParser = |
|
212 |
diskless |
|
213 |
<|> attaching |
|
214 |
<|> failed |
|
215 |
<|> negotiating |
|
216 |
<|> inconsistent |
|
217 |
<|> outdated |
|
218 |
<|> dUnknown |
|
219 |
<|> consistent |
|
220 |
<|> upToDate |
|
221 |
where diskless = A.string "Diskless" *> pure Diskless |
|
222 |
attaching = A.string "Attaching" *> pure Attaching |
|
223 |
failed = A.string "Failed" *> pure Failed |
|
224 |
negotiating = A.string "Negotiating" *> pure Negotiating |
|
225 |
inconsistent = A.string "Inconsistent" *> pure Inconsistent |
|
226 |
outdated = A.string "Outdated" *> pure Outdated |
|
227 |
dUnknown = A.string "DUnknown" *> pure DUnknown |
|
228 |
consistent = A.string "Consistent" *> pure Consistent |
|
229 |
upToDate = A.string "UpToDate" *> pure UpToDate |
|
230 |
|
|
231 |
-- | The parser for I/O flags. |
|
232 |
ioFlagsParser :: Parser String |
|
233 |
ioFlagsParser = fmap unpack . A.takeWhile $ not . isBadEndOfLine |
|
234 |
|
|
235 |
-- | The parser for performance indicators. |
|
236 |
perfIndicatorsParser :: Parser PerfIndicators |
|
237 |
perfIndicatorsParser = |
|
238 |
PerfIndicators |
|
239 |
<$> skipSpacesAndString "ns:" A.decimal |
|
240 |
<*> skipSpacesAndString "nr:" A.decimal |
|
241 |
<*> skipSpacesAndString "dw:" A.decimal |
|
242 |
<*> skipSpacesAndString "dr:" A.decimal |
|
243 |
<*> skipSpacesAndString "al:" A.decimal |
|
244 |
<*> skipSpacesAndString "bm:" A.decimal |
|
245 |
<*> skipSpacesAndString "lo:" A.decimal |
|
246 |
<*> skipSpacesAndString "pe:" A.decimal |
|
247 |
<*> skipSpacesAndString "ua:" A.decimal |
|
248 |
<*> skipSpacesAndString "ap:" A.decimal |
|
249 |
<*> optional (skipSpacesAndString "ep:" A.decimal) |
|
250 |
<*> optional (skipSpacesAndString "wo:" A.anyChar) |
|
251 |
<*> optional (skipSpacesAndString "oos:" A.decimal) |
|
252 |
<* skipSpaces <* A.endOfLine |
|
253 |
|
|
254 |
-- | The parser for the syncronization status. |
|
255 |
syncStatusParser :: Parser SyncStatus |
|
256 |
syncStatusParser = do |
|
257 |
_ <- statusBarParser |
|
258 |
percent <- |
|
259 |
skipSpacesAndString "sync'ed:" $ skipSpaces *> A.double <* A.char '%' |
|
260 |
partSyncSize <- skipSpaces *> A.char '(' *> A.decimal |
|
261 |
totSyncSize <- A.char '/' *> A.decimal <* A.char ')' |
|
262 |
sizeUnit <- sizeUnitParser <* optional A.endOfLine |
|
263 |
timeToEnd <- skipSpacesAndString "finish:" $ skipSpaces *> timeParser |
|
264 |
sp <- |
|
265 |
skipSpacesAndString "speed:" $ |
|
266 |
skipSpaces |
|
267 |
*> commaIntParser |
|
268 |
<* skipSpaces |
|
269 |
<* A.char '(' |
|
270 |
<* commaIntParser |
|
271 |
<* A.char ')' |
|
272 |
w <- skipSpacesAndString "want:" ( |
|
273 |
skipSpaces |
|
274 |
*> (Just <$> commaIntParser) |
|
275 |
) |
|
276 |
<|> pure Nothing |
|
277 |
sSizeUnit <- skipSpaces *> sizeUnitParser |
|
278 |
sTimeUnit <- A.char '/' *> timeUnitParser |
|
279 |
_ <- A.endOfLine |
|
280 |
return $ |
|
281 |
SyncStatus percent partSyncSize totSyncSize sizeUnit timeToEnd sp w |
|
282 |
sSizeUnit sTimeUnit |
|
283 |
|
|
284 |
-- | The parser for recognizing (and discarding) the sync status bar. |
|
285 |
statusBarParser :: Parser () |
|
286 |
statusBarParser = |
|
287 |
skipSpaces |
|
288 |
*> A.char '[' |
|
289 |
*> A.skipWhile (== '=') |
|
290 |
*> A.skipWhile (== '>') |
|
291 |
*> A.skipWhile (== '.') |
|
292 |
*> A.char ']' |
|
293 |
*> pure () |
|
294 |
|
|
295 |
-- | The parser for recognizing data size units (only the ones |
|
296 |
-- actually found in DRBD files are implemented). |
|
297 |
sizeUnitParser :: Parser SizeUnit |
|
298 |
sizeUnitParser = |
|
299 |
kilobyte |
|
300 |
<|> megabyte |
|
301 |
where kilobyte = A.string "K" *> pure KiloByte |
|
302 |
megabyte = A.string "M" *> pure MegaByte |
|
303 |
|
|
304 |
-- | The parser for recognizing time (hh:mm:ss). |
|
305 |
timeParser :: Parser Time |
|
306 |
timeParser = Time <$> h <*> m <*> s |
|
307 |
where h = A.decimal :: Parser Int |
|
308 |
m = A.char ':' *> A.decimal :: Parser Int |
|
309 |
s = A.char ':' *> A.decimal :: Parser Int |
|
310 |
|
|
311 |
-- | The parser for recognizing time units (only the ones actually |
|
312 |
-- found in DRBD files are implemented). |
|
313 |
timeUnitParser :: Parser TimeUnit |
|
314 |
timeUnitParser = second |
|
315 |
where second = A.string "sec" *> pure Second |
|
316 |
|
|
317 |
-- | Haskell does not recognise ',' as the thousands separator every 3 |
|
318 |
-- digits but DRBD uses it, so we need an ah-hoc parser. |
|
319 |
-- If a number beginning with more than 3 digits without a comma is |
|
320 |
-- parsed, only the first 3 digits are considered to be valid, the rest |
|
321 |
-- is not consumed, and left for further parsing. |
|
322 |
commaIntParser :: Parser Int |
|
323 |
commaIntParser = do |
|
324 |
first <- |
|
325 |
AC.count 3 A.digit <|> AC.count 2 A.digit <|> AC.count 1 A.digit |
|
326 |
allDigits <- commaIntHelper (read first) |
|
327 |
pure allDigits |
|
328 |
|
|
329 |
-- | Helper (triplet parser) for the commaIntParser |
|
330 |
commaIntHelper :: Int -> Parser Int |
|
331 |
commaIntHelper acc = nextTriplet <|> end |
|
332 |
where nextTriplet = do |
|
333 |
_ <- A.char ',' |
|
334 |
triplet <- AC.count 3 A.digit |
|
335 |
commaIntHelper $ acc * 1000 + (read triplet :: Int) |
|
336 |
end = pure acc :: Parser Int |
|
337 |
|
|
338 |
-- | Parser for the additional information provided by DRBD <= 8.0. |
|
339 |
additionalInfoParser::Parser AdditionalInfo |
|
340 |
additionalInfoParser = AdditionalInfo |
|
341 |
<$> skipSpacesAndString "used:" A.decimal |
|
342 |
<*> (A.char '/' *> A.decimal) |
|
343 |
<*> skipSpacesAndString "hits:" A.decimal |
|
344 |
<*> skipSpacesAndString "misses:" A.decimal |
|
345 |
<*> skipSpacesAndString "starving:" A.decimal |
|
346 |
<*> skipSpacesAndString "dirty:" A.decimal |
|
347 |
<*> skipSpacesAndString "changed:" A.decimal |
|
348 |
<* A.endOfLine |
/dev/null | ||
---|---|---|
1 |
{-| DRBD Data Types |
|
2 |
|
|
3 |
This module holds the definition of the data types describing the status of |
|
4 |
DRBD. |
|
5 |
|
|
6 |
-} |
|
7 |
{- |
|
8 |
|
|
9 |
Copyright (C) 2012 Google Inc. |
|
10 |
|
|
11 |
This program is free software; you can redistribute it and/or modify |
|
12 |
it under the terms of the GNU General Public License as published by |
|
13 |
the Free Software Foundation; either version 2 of the License, or |
|
14 |
(at your option) any later version. |
|
15 |
|
|
16 |
This program is distributed in the hope that it will be useful, but |
|
17 |
WITHOUT ANY WARRANTY; without even the implied warranty of |
|
18 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
19 |
General Public License for more details. |
|
20 |
|
|
21 |
You should have received a copy of the GNU General Public License |
|
22 |
along with this program; if not, write to the Free Software |
|
23 |
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
|
24 |
02110-1301, USA. |
|
25 |
|
|
26 |
-} |
|
27 |
module Ganeti.Block.Drbd.Types |
|
28 |
( DRBDStatus(..) |
|
29 |
, VersionInfo(..) |
|
30 |
, DeviceInfo(..) |
|
31 |
, ConnState(..) |
|
32 |
, LocalRemote(..) |
|
33 |
, Role(..) |
|
34 |
, DiskState(..) |
|
35 |
, PerfIndicators(..) |
|
36 |
, SyncStatus(..) |
|
37 |
, SizeUnit(..) |
|
38 |
, Time(..) |
|
39 |
, TimeUnit(..) |
|
40 |
, AdditionalInfo(..) |
|
41 |
) where |
|
42 |
|
|
43 |
import Text.JSON |
|
44 |
import Text.Printf |
|
45 |
|
|
46 |
import Ganeti.JSON |
|
47 |
|
|
48 |
--TODO: consider turning deviceInfos into an IntMap |
|
49 |
-- | Data type contaning all the data about the status of DRBD. |
|
50 |
data DRBDStatus = |
|
51 |
DRBDStatus |
|
52 |
{ versionInfo :: VersionInfo -- ^ Version information about DRBD |
|
53 |
, deviceInfos :: [DeviceInfo] -- ^ Per-minor information |
|
54 |
} deriving (Eq, Show) |
|
55 |
|
|
56 |
-- | The DRBDStatus instance of JSON. |
|
57 |
instance JSON DRBDStatus where |
|
58 |
showJSON d = makeObj |
|
59 |
[ ("versionInfo", showJSON $ versionInfo d) |
|
60 |
, ("deviceInfos", showJSONs $ deviceInfos d) |
|
61 |
] |
|
62 |
|
|
63 |
readJSON = error "JSON read instance not implemented for type DRBDStatus" |
|
64 |
|
|
65 |
-- | Data type describing the DRBD version. |
|
66 |
data VersionInfo = |
|
67 |
VersionInfo |
|
68 |
{ version :: Maybe String -- ^ DRBD driver version |
|
69 |
, api :: Maybe String -- ^ The api version |
|
70 |
, proto :: Maybe String -- ^ The protocol version |
|
71 |
, srcversion :: Maybe String -- ^ The version of the source files |
|
72 |
, gitHash :: Maybe String -- ^ Git hash of the source files |
|
73 |
, buildBy :: Maybe String -- ^ Who built the binary (and, |
|
74 |
-- optionally, when) |
|
75 |
} deriving (Eq, Show) |
|
76 |
|
|
77 |
-- | The VersionInfo instance of JSON. |
|
78 |
instance JSON VersionInfo where |
|
79 |
showJSON (VersionInfo versionF apiF protoF srcversionF gitHashF buildByF) = |
|
80 |
optFieldsToObj |
|
81 |
[ optionalJSField "version" versionF |
|
82 |
, optionalJSField "api" apiF |
|
83 |
, optionalJSField "proto" protoF |
|
84 |
, optionalJSField "srcversion" srcversionF |
|
85 |
, optionalJSField "gitHash" gitHashF |
|
86 |
, optionalJSField "buildBy" buildByF |
|
87 |
] |
|
88 |
|
|
89 |
readJSON = error "JSON read instance not implemented for type VersionInfo" |
|
90 |
|
|
91 |
-- | Data type describing a device. |
|
92 |
data DeviceInfo = |
|
93 |
UnconfiguredDevice Int -- ^ An DRBD minor marked as unconfigured |
|
94 |
| -- | A configured DRBD minor |
|
95 |
DeviceInfo |
|
96 |
{ minorNumber :: Int -- ^ The minor index of the device |
|
97 |
, connectionState :: ConnState -- ^ State of the connection |
|
98 |
, resourceRoles :: LocalRemote Role -- ^ Roles of the resources |
|
99 |
, diskStates :: LocalRemote DiskState -- ^ Status of the disks |
|
100 |
, replicationProtocol :: Char -- ^ The replication protocol being used |
|
101 |
, ioFlags :: String -- ^ The input/output flags |
|
102 |
, perfIndicators |
|
103 |
:: PerfIndicators -- ^ Performance indicators |
|
104 |
, syncStatus :: Maybe SyncStatus -- ^ The status of the syncronization of |
|
105 |
-- the disk (only if it is happening) |
|
106 |
, resync :: Maybe AdditionalInfo -- ^ Additional info by DRBD 8.0 |
|
107 |
, actLog :: Maybe AdditionalInfo -- ^ Additional info by DRBD 8.0 |
|
108 |
} deriving (Eq, Show) |
|
109 |
|
|
110 |
-- | The DeviceInfo instance of JSON. |
|
111 |
instance JSON DeviceInfo where |
|
112 |
showJSON (UnconfiguredDevice num) = makeObj |
|
113 |
[ ("minor", showJSON num) |
|
114 |
, ("connectionState", showJSON Unconfigured) |
|
115 |
] |
|
116 |
showJSON (DeviceInfo minorNumberF connectionStateF (LocalRemote |
|
117 |
localRole remoteRole) (LocalRemote localState remoteState) |
|
118 |
replicProtocolF ioFlagsF perfIndicatorsF syncStatusF _ _) = |
|
119 |
optFieldsToObj |
|
120 |
[ Just ("minor", showJSON minorNumberF) |
|
121 |
, Just ("connectionState", showJSON connectionStateF) |
|
122 |
, Just ("localRole", showJSON localRole) |
|
123 |
, Just ("remoteRole", showJSON remoteRole) |
|
124 |
, Just ("localState", showJSON localState) |
|
125 |
, Just ("remoteState", showJSON remoteState) |
|
126 |
, Just ("replicationProtocol", showJSON replicProtocolF) |
|
127 |
, Just ("ioFlags", showJSON ioFlagsF) |
|
128 |
, Just ("perfIndicators", showJSON perfIndicatorsF) |
|
129 |
, optionalJSField "syncStatus" syncStatusF |
|
130 |
] |
|
131 |
|
|
132 |
readJSON = error "JSON read instance not implemented for type DeviceInfo" |
|
133 |
|
|
134 |
-- | Data type describing the state of the connection. |
|
135 |
data ConnState |
|
136 |
= StandAlone -- ^ No network configuration available |
|
137 |
| Disconnecting -- ^ Temporary state during disconnection |
|
138 |
| Unconnected -- ^ Prior to a connection attempt |
|
139 |
| Timeout -- ^ Following a timeout in the communication |
|
140 |
| BrokenPipe -- ^ After the connection to the peer was lost |
|
141 |
| NetworkFailure -- ^ After the connection to the parner was lost |
|
142 |
| ProtocolError -- ^ After the connection to the parner was lost |
|
143 |
| TearDown -- ^ The peer is closing the connection |
|
144 |
| WFConnection -- ^ Waiting for the peer to become visible |
|
145 |
| WFReportParams -- ^ Waiting for first packet from peer |
|
146 |
| Connected -- ^ Connected, data mirroring active |
|
147 |
| StartingSyncS -- ^ Source of a full sync started by admin |
|
148 |
| StartingSyncT -- ^ Target of a full sync started by admin |
|
149 |
| WFBitMapS -- ^ Source of a just starting partial sync |
|
150 |
| WFBitMapT -- ^ Target of a just starting partial sync |
|
151 |
| WFSyncUUID -- ^ Synchronization is about to begin |
|
152 |
| SyncSource -- ^ Source of a running synchronization |
|
153 |
| SyncTarget -- ^ Target of a running synchronization |
|
154 |
| PausedSyncS -- ^ Source of a paused synchronization |
|
155 |
| PausedSyncT -- ^ Target of a paused synchronization |
|
156 |
| VerifyS -- ^ Source of an running verification |
|
157 |
| VerifyT -- ^ Target of an running verification |
|
158 |
| Unconfigured -- ^ The device is not configured |
|
159 |
deriving (Show, Eq) |
|
160 |
|
|
161 |
-- | The ConnState instance of JSON. |
|
162 |
instance JSON ConnState where |
|
163 |
showJSON = showJSON . show |
|
164 |
|
|
165 |
readJSON = error "JSON read instance not implemented for type ConnState" |
|
166 |
|
|
167 |
-- | Algebraic data type describing something that has a local and a remote |
|
168 |
-- value. |
|
169 |
data LocalRemote a = |
|
170 |
LocalRemote |
|
171 |
{ local :: a -- ^ The local value |
|
172 |
, remote :: a -- ^ The remote value |
|
173 |
} deriving (Eq, Show) |
|
174 |
|
|
175 |
-- | Data type describing. |
|
176 |
data Role = Primary -- ^ The device role is primary |
|
177 |
| Secondary -- ^ The device role is secondary |
|
178 |
| Unknown -- ^ The device role is unknown |
|
179 |
deriving (Eq, Show) |
|
180 |
|
|
181 |
-- | The Role instance of JSON. |
|
182 |
instance JSON Role where |
|
183 |
showJSON = showJSON . show |
|
184 |
|
|
185 |
readJSON = error "JSON read instance not implemented for type Role" |
|
186 |
|
|
187 |
-- | Data type describing disk states. |
|
188 |
data DiskState |
|
189 |
= Diskless -- ^ No local block device assigned to the DRBD driver |
|
190 |
| Attaching -- ^ Reading meta data |
|
191 |
| Failed -- ^ I/O failure |
|
192 |
| Negotiating -- ^ "Attach" on an already-connected device |
|
193 |
| Inconsistent -- ^ The data is inconsistent between nodes. |
|
194 |
| Outdated -- ^ Data consistent but outdated |
|
195 |
| DUnknown -- ^ No network connection available |
|
196 |
| Consistent -- ^ Consistent data, but without network connection |
|
197 |
| UpToDate -- ^ Consistent, up-to-date. This is the normal state |
|
198 |
deriving (Eq, Show) |
|
199 |
|
|
200 |
-- | The DiskState instance of JSON. |
|
201 |
instance JSON DiskState where |
|
202 |
showJSON = showJSON . show |
|
203 |
|
|
204 |
readJSON = error "JSON read instance not implemented for type DiskState" |
|
205 |
|
|
206 |
-- | Data type containing data about performance indicators. |
|
207 |
data PerfIndicators = PerfIndicators |
|
208 |
{ networkSend :: Int -- ^ KiB of data sent on the network |
|
209 |
, networkReceive :: Int -- ^ KiB of data received from the network |
|
210 |
, diskWrite :: Int -- ^ KiB of data written on local disk |
|
211 |
, diskRead :: Int -- ^ KiB of data read from local disk |
|
212 |
, activityLog :: Int -- ^ Number of updates of the activity log |
|
213 |
, bitMap :: Int -- ^ Number of updates to the bitmap area of the metadata |
|
214 |
, localCount :: Int -- ^ Number of open requests to te local I/O subsystem |
|
215 |
, pending :: Int -- ^ Num of requests sent to the partner but not yet answered |
|
216 |
, unacknowledged :: Int -- ^ Num of requests received by the partner but still |
|
217 |
-- to be answered |
|
218 |
, applicationPending :: Int -- ^ Num of block I/O requests forwarded |
|
219 |
-- to DRBD but that have not yet been |
|
220 |
-- answered |
|
221 |
, epochs :: Maybe Int -- ^ Number of epoch objects |
|
222 |
, writeOrder :: Maybe Char -- ^ Currently used write ordering method |
|
223 |
, outOfSync :: Maybe Int -- ^ KiB of storage currently out of sync |
|
224 |
} deriving (Eq, Show) |
|
225 |
|
|
226 |
-- | The PerfIndicators instance of JSON. |
|
227 |
instance JSON PerfIndicators where |
|
228 |
showJSON p = optFieldsToObj |
|
229 |
[ Just ("networkSend", showJSON $ networkSend p) |
|
230 |
, Just ("networkReceive", showJSON $ networkReceive p) |
|
231 |
, Just ("diskWrite", showJSON $ diskWrite p) |
|
232 |
, Just ("diskRead", showJSON $ diskRead p) |
|
233 |
, Just ("activityLog", showJSON $ activityLog p) |
|
234 |
, Just ("bitMap", showJSON $ bitMap p) |
|
235 |
, Just ("localCount", showJSON $ localCount p) |
|
236 |
, Just ("pending", showJSON $ pending p) |
|
237 |
, Just ("unacknowledged", showJSON $ unacknowledged p) |
|
238 |
, Just ("applicationPending", showJSON $ applicationPending p) |
|
239 |
, optionalJSField "epochs" $ epochs p |
|
240 |
, optionalJSField "writeOrder" $ writeOrder p |
|
241 |
, optionalJSField "outOfSync" $ outOfSync p |
|
242 |
] |
|
243 |
|
|
244 |
readJSON = error "JSON read instance not implemented for type PerfIndicators" |
|
245 |
|
|
246 |
-- | Data type containing data about the synchronization status of a device. |
|
247 |
data SyncStatus = |
|
248 |
SyncStatus |
|
249 |
{ percentage :: Double -- ^ Percentage of syncronized data |
|
250 |
, partialSyncSize :: Int -- ^ Numerator of the fraction of synced data |
|
251 |
, totalSyncSize :: Int -- ^ Denominator of the fraction of |
|
252 |
-- synced data |
|
253 |
, syncUnit :: SizeUnit -- ^ Measurement unit of the previous |
|
254 |
-- fraction |
|
255 |
, timeToFinish :: Time -- ^ Expected time before finishing |
|
256 |
-- the syncronization |
|
257 |
, speed :: Int -- ^ Speed of the syncronization |
|
258 |
, want :: Maybe Int -- ^ Want of the syncronization |
|
259 |
, speedSizeUnit :: SizeUnit -- ^ Size unit of the speed |
|
260 |
, speedTimeUnit :: TimeUnit -- ^ Time unit of the speed |
|
261 |
} deriving (Eq, Show) |
|
262 |
|
|
263 |
-- | The SyncStatus instance of JSON. |
|
264 |
instance JSON SyncStatus where |
|
265 |
showJSON s = optFieldsToObj |
|
266 |
[ Just ("percentage", showJSON $ percentage s) |
|
267 |
, Just ("progress", showJSON $ show (partialSyncSize s) ++ "/" ++ |
|
268 |
show (totalSyncSize s)) |
|
269 |
, Just ("progressUnit", showJSON $ syncUnit s) |
|
270 |
, Just ("timeToFinish", showJSON $ timeToFinish s) |
|
271 |
, Just ("speed", showJSON $ speed s) |
|
272 |
, optionalJSField "want" $ want s |
|
273 |
, Just ("speedUnit", showJSON $ show (speedSizeUnit s) ++ "/" ++ |
|
274 |
show (speedTimeUnit s)) |
|
275 |
] |
|
276 |
|
|
277 |
readJSON = error "JSON read instance not implemented for type SyncStatus" |
|
278 |
|
|
279 |
-- | Data type describing a size unit for memory. |
|
280 |
data SizeUnit = KiloByte | MegaByte deriving (Eq, Show) |
|
281 |
|
|
282 |
-- | The SizeUnit instance of JSON. |
|
283 |
instance JSON SizeUnit where |
|
284 |
showJSON = showJSON . show |
|
285 |
|
|
286 |
readJSON = error "JSON read instance not implemented for type SizeUnit" |
|
287 |
|
|
288 |
-- | Data type describing a time (hh:mm:ss). |
|
289 |
data Time = Time |
|
290 |
{ hour :: Int |
|
291 |
, min :: Int |
|
292 |
, sec :: Int |
|
293 |
} deriving (Eq, Show) |
|
294 |
|
|
295 |
-- | The Time instance of JSON. |
|
296 |
instance JSON Time where |
|
297 |
showJSON (Time h m s) = showJSON (printf "%02d:%02d:%02d" h m s :: String) |
|
298 |
|
|
299 |
readJSON = error "JSON read instance not implemented for type Time" |
|
300 |
|
|
301 |
-- | Data type describing a time unit. |
|
302 |
data TimeUnit = Second deriving (Eq, Show) |
|
303 |
|
|
304 |
-- | The TimeUnit instance of JSON. |
|
305 |
instance JSON TimeUnit where |
|
306 |
showJSON Second = showJSON "Second" |
|
307 |
|
|
308 |
readJSON = error "JSON read instance not implemented for type TimeUnit" |
|
309 |
|
|
310 |
-- | Additional device-specific cache-like information produced by |
|
311 |
-- drbd <= 8.0. |
|
312 |
-- |
|
313 |
-- Internal debug information exported by old DRBD versions. |
|
314 |
-- Undocumented both in DRBD and here. |
|
315 |
data AdditionalInfo = AdditionalInfo |
|
316 |
{ partialUsed :: Int |
|
317 |
, totalUsed :: Int |
|
318 |
, hits :: Int |
|
319 |
, misses :: Int |
|
320 |
, starving :: Int |
|
321 |
, dirty :: Int |
|
322 |
, changed :: Int |
|
323 |
} deriving (Eq, Show) |
/dev/null | ||
---|---|---|
1 |
{-| Base common functionality. |
|
2 |
|
|
3 |
This module holds common functionality shared across Ganeti daemons, |
|
4 |
HTools and any other programs. |
|
5 |
|
|
6 |
-} |
|
7 |
|
|
8 |
{- |
|
9 |
|
|
10 |
Copyright (C) 2009, 2010, 2011, 2012 Google Inc. |
|
11 |
|
|
12 |
This program is free software; you can redistribute it and/or modify |
|
13 |
it under the terms of the GNU General Public License as published by |
|
14 |
the Free Software Foundation; either version 2 of the License, or |
|
15 |
(at your option) any later version. |
|
16 |
|
|
17 |
This program is distributed in the hope that it will be useful, but |
|
18 |
WITHOUT ANY WARRANTY; without even the implied warranty of |
|
19 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
20 |
General Public License for more details. |
|
21 |
|
|
22 |
You should have received a copy of the GNU General Public License |
|
23 |
along with this program; if not, write to the Free Software |
|
24 |
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
|
25 |
02110-1301, USA. |
|
26 |
|
|
27 |
-} |
|
28 |
|
|
29 |
module Ganeti.Common |
|
30 |
( GenericOptType |
|
31 |
, StandardOptions(..) |
|
32 |
, OptCompletion(..) |
|
33 |
, ArgCompletion(..) |
|
34 |
, PersonalityList |
|
35 |
, optComplYesNo |
|
36 |
, oShowHelp |
|
37 |
, oShowVer |
|
38 |
, oShowComp |
|
39 |
, usageHelp |
|
40 |
, versionInfo |
|
41 |
, formatCommands |
|
42 |
, reqWithConversion |
|
43 |
, parseYesNo |
|
44 |
, parseOpts |
|
45 |
, parseOptsInner |
|
46 |
, parseOptsCmds |
|
47 |
, genericMainCmds |
|
48 |
) where |
|
49 |
|
|
50 |
import Control.Monad (foldM) |
|
51 |
import Data.Char (toLower) |
|
52 |
import Data.List (intercalate, stripPrefix, sortBy) |
|
53 |
import Data.Maybe (fromMaybe) |
|
54 |
import Data.Ord (comparing) |
|
55 |
import qualified Data.Version |
|
56 |
import System.Console.GetOpt |
|
57 |
import System.Environment |
|
58 |
import System.Exit |
|
59 |
import System.Info |
Also available in: Unified diff