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
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff