Revision d78970ba
b/Makefile.am | ||
---|---|---|
914 | 914 |
test/data/bdev-drbd-net-ip6.txt \ |
915 | 915 |
test/data/cert1.pem \ |
916 | 916 |
test/data/cert2.pem \ |
917 |
test/data/instance-minor-pairing.txt \ |
|
917 | 918 |
test/data/ip-addr-show-dummy0.txt \ |
918 | 919 |
test/data/ip-addr-show-lo-ipv4.txt \ |
919 | 920 |
test/data/ip-addr-show-lo-ipv6.txt \ |
b/man/mon-collector.rst | ||
---|---|---|
33 | 33 |
DRBD |
34 | 34 |
~~~~ |
35 | 35 |
|
36 |
| drbd [*status-file*]
|
|
36 |
| drbd [ [ **-s** | **\--drbd-status** ] = *status-file* ] [ [ **-p** | **\--drbd-pairing**] = *pairing-file* ]
|
|
37 | 37 |
|
38 | 38 |
Collects the information about the version and status of the DRBD kernel |
39 | 39 |
module, and of the disks it is managing. |
40 | 40 |
|
41 |
If *status-file* is specified, the status will be read from that file. |
|
42 |
Otherwise, the collector will read it from /proc/drbd. |
|
41 |
If *status-file* and *pairing-file* are specified, the status and the |
|
42 |
instance-minor paring information will be read from those files. |
|
43 |
Otherwise, the collector will read them, respectively, from /proc/drbd |
|
44 |
and from the Confd server. |
|
45 |
|
|
46 |
The options that can be passed to the DRBD collector are as follows: |
|
47 |
|
|
48 |
-s *status-file*, \--drbd-status=*status-file* |
|
49 |
Read the DRBD status from the specified file instead of /proc/drbd. |
|
50 |
|
|
51 |
-p *pairing-file*, \--drbd-pairing=*pairing-file* |
|
52 |
Read the information about the pairing between instances and DRBD minors |
|
53 |
from the specified file instead of asking the Confd servers for them. |
b/src/Ganeti/DataCollectors/CLI.hs | ||
---|---|---|
34 | 34 |
, oShowHelp |
35 | 35 |
, oShowVer |
36 | 36 |
, oShowComp |
37 |
, oDrbdPairing |
|
38 |
, oDrbdStatus |
|
37 | 39 |
, genericOptions |
38 | 40 |
) where |
39 | 41 |
|
42 |
import System.Console.GetOpt |
|
43 |
|
|
44 |
import Ganeti.BasicTypes |
|
40 | 45 |
import Ganeti.Common as Common |
41 | 46 |
|
42 | 47 |
-- * Data types |
... | ... | |
46 | 51 |
{ optShowHelp :: Bool -- ^ Just show the help |
47 | 52 |
, optShowComp :: Bool -- ^ Just show the completion info |
48 | 53 |
, optShowVer :: Bool -- ^ Just show the program version |
54 |
, optDrbdStatus :: Maybe FilePath -- ^ Path to the file containing DRBD |
|
55 |
-- status information |
|
56 |
, optDrbdPairing :: Maybe FilePath -- ^ Path to the file containing pairings |
|
57 |
-- between instances and DRBD minors |
|
49 | 58 |
} deriving Show |
50 | 59 |
|
51 | 60 |
-- | Default values for the command line options. |
... | ... | |
54 | 63 |
{ optShowHelp = False |
55 | 64 |
, optShowComp = False |
56 | 65 |
, optShowVer = False |
66 |
, optDrbdStatus = Nothing |
|
67 |
, optDrbdPairing = Nothing |
|
57 | 68 |
} |
58 | 69 |
|
59 | 70 |
-- | Abbreviation for the option type. |
... | ... | |
68 | 79 |
requestComp o = o { optShowComp = True } |
69 | 80 |
|
70 | 81 |
-- * Command line options |
82 |
oDrbdPairing :: OptType |
|
83 |
oDrbdPairing = |
|
84 |
( Option "p" ["drbd-pairing"] |
|
85 |
(ReqArg (\ f o -> Ok o { optDrbdPairing = Just f}) "FILE") |
|
86 |
"the FILE containing pairings between instances and DRBD minors", |
|
87 |
OptComplFile) |
|
88 |
|
|
89 |
oDrbdStatus :: OptType |
|
90 |
oDrbdStatus = |
|
91 |
( Option "s" ["drbd-status"] |
|
92 |
(ReqArg (\ f o -> Ok o { optDrbdStatus = Just f }) "FILE") |
|
93 |
"the DRBD status FILE", |
|
94 |
OptComplFile) |
|
71 | 95 |
|
72 | 96 |
-- | Generic options. |
73 | 97 |
genericOptions :: [GenericOptType Options] |
b/src/Ganeti/DataCollectors/Drbd.hs | ||
---|---|---|
31 | 31 |
|
32 | 32 |
|
33 | 33 |
import qualified Control.Exception as E |
34 |
import Control.Monad |
|
34 | 35 |
import Data.Attoparsec.Text.Lazy as A |
36 |
import Data.Maybe |
|
35 | 37 |
import Data.Text.Lazy (pack, unpack) |
36 |
import Text.JSON |
|
38 |
import Network.BSD (getHostName) |
|
39 |
import qualified Text.JSON as J |
|
37 | 40 |
|
38 | 41 |
import qualified Ganeti.BasicTypes as BT |
39 | 42 |
import qualified Ganeti.Constants as C |
40 | 43 |
import Ganeti.Block.Drbd.Parser(drbdStatusParser) |
44 |
import Ganeti.Block.Drbd.Types(DrbdInstMinor) |
|
41 | 45 |
import Ganeti.Common |
46 |
import Ganeti.Confd.Client |
|
47 |
import Ganeti.Confd.Types |
|
42 | 48 |
import Ganeti.DataCollectors.CLI |
43 | 49 |
import Ganeti.Utils |
44 | 50 |
|
... | ... | |
56 | 62 |
defaultCharNum = 80*20 |
57 | 63 |
|
58 | 64 |
options :: IO [OptType] |
59 |
options = return [] |
|
65 |
options = |
|
66 |
return |
|
67 |
[ oDrbdStatus |
|
68 |
, oDrbdPairing |
|
69 |
] |
|
60 | 70 |
|
61 | 71 |
-- | The list of arguments supported by the program. |
62 | 72 |
arguments :: [ArgCompletion] |
63 |
arguments = [ArgCompletion OptComplFile 0 (Just 1)]
|
|
73 |
arguments = [ArgCompletion OptComplFile 0 (Just 0)]
|
|
64 | 74 |
|
65 | 75 |
-- * Command line options |
66 | 76 |
|
77 |
-- | Get information about the pairing of DRBD minors and Ganeti instances |
|
78 |
-- on the current node. The information is taken from the Confd client |
|
79 |
-- or, if a filename is specified, from a JSON encoded file (for testing |
|
80 |
-- purposes). |
|
81 |
getPairingInfo :: Maybe String -> IO (BT.Result [DrbdInstMinor]) |
|
82 |
getPairingInfo Nothing = do |
|
83 |
curNode <- getHostName |
|
84 |
client <- getConfdClient |
|
85 |
reply <- query client ReqNodeDrbd $ PlainQuery curNode |
|
86 |
return $ |
|
87 |
case fmap (J.readJSONs . confdReplyAnswer) reply of |
|
88 |
Just (J.Ok instMinor) -> BT.Ok instMinor |
|
89 |
Just (J.Error msg) -> BT.Bad msg |
|
90 |
Nothing -> BT.Bad "No answer from the Confd server" |
|
91 |
getPairingInfo (Just filename) = do |
|
92 |
content <- readFile filename |
|
93 |
return $ |
|
94 |
case J.decode content of |
|
95 |
J.Ok instMinor -> BT.Ok instMinor |
|
96 |
J.Error msg -> BT.Bad msg |
|
97 |
|
|
67 | 98 |
-- | Main function. |
68 | 99 |
main :: Options -> [String] -> IO () |
69 |
main _ args = do |
|
70 |
proc_drbd <- case args of |
|
71 |
[ ] -> return defaultFile |
|
72 |
[x] -> return x |
|
73 |
_ -> exitErr $ "This program takes only one argument," ++ |
|
74 |
" got '" ++ unwords args ++ "'" |
|
100 |
main opts args = do |
|
101 |
let proc_drbd = fromMaybe defaultFile $ optDrbdStatus opts |
|
102 |
instMinor = optDrbdPairing opts |
|
103 |
unless (null args) . exitErr $ "This program takes exactly zero" ++ |
|
104 |
" arguments, got '" ++ unwords args ++ "'" |
|
75 | 105 |
contents <- |
76 | 106 |
((E.try $ readFile proc_drbd) :: IO (Either IOError String)) >>= |
77 | 107 |
exitIfBad "reading from file" . either (BT.Bad . show) BT.Ok |
108 |
pairingResult <- getPairingInfo instMinor |
|
109 |
pairing <- exitIfBad "Can't get pairing info" pairingResult |
|
78 | 110 |
output <- |
79 |
case A.parse (drbdStatusParser []) $ pack contents of
|
|
111 |
case A.parse (drbdStatusParser pairing) $ pack contents of
|
|
80 | 112 |
A.Fail unparsedText contexts errorMessage -> exitErr $ |
81 | 113 |
show (Prelude.take defaultCharNum $ unpack unparsedText) ++ "\n" |
82 | 114 |
++ show contexts ++ "\n" ++ errorMessage |
83 |
A.Done _ drbdStatus -> return $ encode drbdStatus |
|
115 |
A.Done _ drbdStatus -> return $ J.encode drbdStatus
|
|
84 | 116 |
putStrLn output |
b/test/data/instance-minor-pairing.txt | ||
---|---|---|
1 |
[["machine1.example.com",0,"instance1.example.com","disk/0","secondary", |
|
2 |
"machine2.example.com"]] |
b/test/hs/shelltests/htools-mon-collector.test | ||
---|---|---|
23 | 23 |
>>>= 0 |
24 | 24 |
|
25 | 25 |
# 3. Test that the drbd collector fails parsing /dev/null |
26 |
./test/hs/hpc-mon-collector drbd /dev/null |
|
27 |
>>>2 |
|
28 |
Error: "" |
|
29 |
[] |
|
30 |
Failed reading: versionInfo |
|
26 |
./test/hs/hpc-mon-collector drbd --drbd-status=/dev/null --drbd-pairing=/dev/null |
|
27 |
>>>2/Malformed JSON/ |
|
31 | 28 |
>>>= !0 |
32 | 29 |
|
33 | 30 |
# 4. Test that a non-existent file is correctly reported |
34 |
./test/hs/hpc-mon-collector drbd /dev/no-such-file |
|
31 |
./test/hs/hpc-mon-collector drbd --drbd-status=/dev/no-such-file --drbd-pairing=/dev/no-such-file
|
|
35 | 32 |
>>>2/Error: reading from file: .* does not exist/ |
36 | 33 |
>>>= !0 |
37 | 34 |
|
38 |
# 5. Test that multiple files are rejected
|
|
39 |
./test/hs/hpc-mon-collector drbd /dev/null /dev/null
|
|
40 |
>>>2/takes only one argument/
|
|
35 |
# 5. Test that arguments are rejected
|
|
36 |
./test/hs/hpc-mon-collector drbd /dev/null |
|
37 |
>>>2/takes exactly zero arguments/
|
|
41 | 38 |
>>>= !0 |
42 | 39 |
|
43 | 40 |
# 6. Test that a standard test file is parsed correctly |
44 |
./test/hs/hpc-mon-collector drbd $PYTESTDATA_DIR/proc_drbd83.txt
|
|
41 |
./test/hs/hpc-mon-collector drbd --drbd-status=$PYTESTDATA_DIR/proc_drbd83.txt --drbd-pairing=$PYTESTDATA_DIR/instance-minor-pairing.txt
|
|
45 | 42 |
>>>=0 |
46 | 43 |
|
47 | 44 |
# 7. Test that the drbd collector fails parsing /dev/zero, but is not |
48 | 45 |
# stuck forever printing \NUL chars |
49 |
./test/hs/hpc-mon-collector drbd /dev/zero
|
|
46 |
./test/hs/hpc-mon-collector drbd --drbd-status=/dev/zero --drbd-pairing=$PYTESTDATA_DIR/instance-minor-pairing.txt
|
|
50 | 47 |
>>>2 |
51 | 48 |
Error: "\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL\NUL" |
52 | 49 |
[] |
Also available in: Unified diff