Revision b8585908
b/Makefile.am | ||
---|---|---|
65 | 65 |
src/Ganeti/HTools \ |
66 | 66 |
src/Ganeti/HTools/Backend \ |
67 | 67 |
src/Ganeti/HTools/Program \ |
68 |
src/Ganeti/Hypervisor \ |
|
69 |
src/Ganeti/Hypervisor/Xen \ |
|
68 | 70 |
src/Ganeti/Query \ |
69 | 71 |
test/hs \ |
70 | 72 |
test/hs/Test \ |
... | ... | |
74 | 76 |
test/hs/Test/Ganeti/Confd \ |
75 | 77 |
test/hs/Test/Ganeti/HTools \ |
76 | 78 |
test/hs/Test/Ganeti/HTools/Backend \ |
79 |
test/hs/Test/Ganeti/Hypervisor \ |
|
80 |
test/hs/Test/Ganeti/Hypervisor/Xen \ |
|
77 | 81 |
test/hs/Test/Ganeti/Query |
78 | 82 |
|
79 | 83 |
DIRS = \ |
... | ... | |
122 | 126 |
$(APIDOC_HS_DIR)/Ganeti/HTools \ |
123 | 127 |
$(APIDOC_HS_DIR)/Ganeti/HTools/Backend \ |
124 | 128 |
$(APIDOC_HS_DIR)/Ganeti/HTools/Program \ |
129 |
$(APIDOC_HS_DIR)/Ganeti/Hypervisor \ |
|
130 |
$(APIDOC_HS_DIR)/Ganeti/Hypervisor/Xen \ |
|
125 | 131 |
$(APIDOC_HS_DIR)/Ganeti/Query |
126 | 132 |
|
127 | 133 |
BUILDTIME_DIR_AUTOCREATE = \ |
... | ... | |
517 | 523 |
src/Ganeti/HTools/Program/Hroller.hs \ |
518 | 524 |
src/Ganeti/HTools/Program/Main.hs \ |
519 | 525 |
src/Ganeti/HTools/Types.hs \ |
526 |
src/Ganeti/Hypervisor/Xen/XmParser.hs \ |
|
527 |
src/Ganeti/Hypervisor/Xen/Types.hs \ |
|
520 | 528 |
src/Ganeti/Hash.hs \ |
521 | 529 |
src/Ganeti/JQueue.hs \ |
522 | 530 |
src/Ganeti/JSON.hs \ |
... | ... | |
566 | 574 |
test/hs/Test/Ganeti/HTools/Node.hs \ |
567 | 575 |
test/hs/Test/Ganeti/HTools/PeerMap.hs \ |
568 | 576 |
test/hs/Test/Ganeti/HTools/Types.hs \ |
577 |
test/hs/Test/Ganeti/Hypervisor/Xen/XmParser.hs \ |
|
569 | 578 |
test/hs/Test/Ganeti/JSON.hs \ |
570 | 579 |
test/hs/Test/Ganeti/Jobs.hs \ |
571 | 580 |
test/hs/Test/Ganeti/JQueue.hs \ |
... | ... | |
1060 | 1069 |
test/data/xen-xm-info-4.0.1.txt \ |
1061 | 1070 |
test/data/xen-xm-list-4.0.1-dom0-only.txt \ |
1062 | 1071 |
test/data/xen-xm-list-4.0.1-four-instances.txt \ |
1072 |
test/data/xen-xm-list-long-4.0.1.txt \ |
|
1063 | 1073 |
test/py/ganeti-cli.test \ |
1064 | 1074 |
test/py/gnt-cli.test \ |
1065 | 1075 |
test/py/import-export_unittest-helper |
b/src/Ganeti/Hypervisor/Xen/Types.hs | ||
---|---|---|
1 |
{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-} |
|
2 |
{-| Data types for Xen-specific hypervisor functionalities. |
|
3 |
|
|
4 |
-} |
|
5 |
{- |
|
6 |
|
|
7 |
Copyright (C) 2013 Google Inc. |
|
8 |
|
|
9 |
This program is free software; you can redistribute it and/or modify |
|
10 |
it under the terms of the GNU General Public License as published by |
|
11 |
the Free Software Foundation; either version 2 of the License, or |
|
12 |
(at your option) any later version. |
|
13 |
|
|
14 |
This program is distributed in the hope that it will be useful, but |
|
15 |
WITHOUT ANY WARRANTY; without even the implied warranty of |
|
16 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
17 |
General Public License for more details. |
|
18 |
|
|
19 |
You should have received a copy of the GNU General Public License |
|
20 |
along with this program; if not, write to the Free Software |
|
21 |
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
|
22 |
02110-1301, USA. |
|
23 |
|
|
24 |
-} |
|
25 |
module Ganeti.Hypervisor.Xen.Types |
|
26 |
( LispConfig(..) |
|
27 |
, Domain(..) |
|
28 |
, FromLispConfig(..) |
|
29 |
, ActualState(..) |
|
30 |
) where |
|
31 |
|
|
32 |
import qualified Text.JSON as J |
|
33 |
|
|
34 |
import Ganeti.BasicTypes |
|
35 |
|
|
36 |
-- | Data type representing configuration data as produced by the |
|
37 |
-- @xm list --long@ command. |
|
38 |
data LispConfig = LCList [LispConfig] |
|
39 |
| LCString String |
|
40 |
| LCDouble Double |
|
41 |
deriving (Eq, Show) |
|
42 |
|
|
43 |
-- | Data type representing a Xen Domain. |
|
44 |
data Domain = Domain |
|
45 |
{ domId :: Int |
|
46 |
, domName :: String |
|
47 |
, domCpuTime :: Double |
|
48 |
, domState :: ActualState |
|
49 |
, domIsHung :: Maybe Bool |
|
50 |
} deriving (Show, Eq) |
|
51 |
|
|
52 |
-- | Class representing all the types that can be extracted from LispConfig. |
|
53 |
class FromLispConfig a where |
|
54 |
fromLispConfig :: LispConfig -> Result a |
|
55 |
|
|
56 |
-- | Instance of FromLispConfig for Int. |
|
57 |
instance FromLispConfig Int where |
|
58 |
fromLispConfig (LCDouble d) = Ok $ floor d |
|
59 |
fromLispConfig (LCList (LCString _:LCDouble d:[])) = Ok $ floor d |
|
60 |
fromLispConfig c = |
|
61 |
Bad $ "Unable to extract a Int from this configuration: " |
|
62 |
++ show c |
|
63 |
|
|
64 |
-- | Instance of FromLispConfig for Double. |
|
65 |
instance FromLispConfig Double where |
|
66 |
fromLispConfig (LCDouble d) = Ok d |
|
67 |
fromLispConfig (LCList (LCString _:LCDouble d:[])) = Ok d |
|
68 |
fromLispConfig c = |
|
69 |
Bad $ "Unable to extract a Double from this configuration: " |
|
70 |
++ show c |
|
71 |
|
|
72 |
-- | Instance of FromLispConfig for String |
|
73 |
instance FromLispConfig String where |
|
74 |
fromLispConfig (LCString s) = Ok s |
|
75 |
fromLispConfig (LCList (LCString _:LCString s:[])) = Ok s |
|
76 |
fromLispConfig c = |
|
77 |
Bad $ "Unable to extract a String from this configuration: " |
|
78 |
++ show c |
|
79 |
|
|
80 |
-- | Instance of FromLispConfig for [LispConfig] |
|
81 |
instance FromLispConfig [LispConfig] where |
|
82 |
fromLispConfig (LCList l) = Ok l |
|
83 |
fromLispConfig c = |
|
84 |
Bad $ "Unable to extract a List from this configuration: " |
|
85 |
++ show c |
|
86 |
|
|
87 |
data ActualState = ActualRunning -- ^ The instance is running |
|
88 |
| ActualBlocked -- ^ The instance is not running or runnable |
|
89 |
| ActualPaused -- ^ The instance has been paused |
|
90 |
| ActualShutdown -- ^ The instance is shut down |
|
91 |
| ActualCrashed -- ^ The instance has crashed |
|
92 |
| ActualDying -- ^ The instance is in process of dying |
|
93 |
| ActualHung -- ^ The instance is hung |
|
94 |
| ActualUnknown -- ^ Unknown state. Parsing error. |
|
95 |
deriving (Show, Eq) |
|
96 |
|
|
97 |
instance J.JSON ActualState where |
|
98 |
showJSON ActualRunning = J.showJSON "running" |
|
99 |
showJSON ActualBlocked = J.showJSON "blocked" |
|
100 |
showJSON ActualPaused = J.showJSON "paused" |
|
101 |
showJSON ActualShutdown = J.showJSON "shutdown" |
|
102 |
showJSON ActualCrashed = J.showJSON "crashed" |
|
103 |
showJSON ActualDying = J.showJSON "dying" |
|
104 |
showJSON ActualHung = J.showJSON "hung" |
|
105 |
showJSON ActualUnknown = J.showJSON "unknown" |
|
106 |
|
|
107 |
readJSON = error "JSON read instance not implemented for type ActualState" |
b/src/Ganeti/Hypervisor/Xen/XmParser.hs | ||
---|---|---|
1 |
{-# LANGUAGE OverloadedStrings #-} |
|
2 |
{-| Parser for the output of the @xm list --long@ command of Xen |
|
3 |
|
|
4 |
-} |
|
5 |
{- |
|
6 |
|
|
7 |
Copyright (C) 2013 Google Inc. |
|
8 |
|
|
9 |
This program is free software; you can redistribute it and/or modify |
|
10 |
it under the terms of the GNU General Public License as published by |
|
11 |
the Free Software Foundation; either version 2 of the License, or |
|
12 |
(at your option) any later version. |
|
13 |
|
|
14 |
This program is distributed in the hope that it will be useful, but |
|
15 |
WITHOUT ANY WARRANTY; without even the implied warranty of |
|
16 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
17 |
General Public License for more details. |
|
18 |
|
|
19 |
You should have received a copy of the GNU General Public License |
|
20 |
along with this program; if not, write to the Free Software |
|
21 |
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
|
22 |
02110-1301, USA. |
|
23 |
|
|
24 |
-} |
|
25 |
module Ganeti.Hypervisor.Xen.XmParser |
|
26 |
( xmListParser |
|
27 |
, lispConfigParser |
|
28 |
) where |
|
29 |
|
|
30 |
import Control.Applicative |
|
31 |
import Control.Monad |
|
32 |
import qualified Data.Attoparsec.Combinator as AC |
|
33 |
import qualified Data.Attoparsec.Text as A |
|
34 |
import Data.Attoparsec.Text (Parser) |
|
35 |
import Data.Char |
|
36 |
import Data.List |
|
37 |
import Data.Text (unpack) |
|
38 |
import qualified Data.Map as Map |
|
39 |
|
|
40 |
import Ganeti.BasicTypes |
|
41 |
import Ganeti.Hypervisor.Xen.Types |
|
42 |
|
|
43 |
-- | A parser for parsing generic config files written in the (LISP-like) |
|
44 |
-- format that is the output of the @xm list --long@ command. |
|
45 |
-- This parser only takes care of the syntactic parse, but does not care |
|
46 |
-- about the semantics. |
|
47 |
-- Note: parsing the double requires checking for the next character in order |
|
48 |
-- to prevent string like "9a" to be recognized as the number 9. |
|
49 |
lispConfigParser :: Parser LispConfig |
|
50 |
lispConfigParser = |
|
51 |
A.skipSpace *> |
|
52 |
( listConfigP |
|
53 |
<|> doubleP |
|
54 |
<|> stringP |
|
55 |
) |
|
56 |
<* A.skipSpace |
|
57 |
where listConfigP = LCList <$> (A.char '(' *> liftA2 (++) |
|
58 |
(many middleP) |
|
59 |
(((:[]) <$> finalP) <|> (rparen *> pure []))) |
|
60 |
doubleP = LCDouble <$> A.double <* A.skipSpace <* A.endOfInput |
|
61 |
innerDoubleP = LCDouble <$> A.double |
|
62 |
stringP = LCString . unpack <$> A.takeWhile1 (not . (\c -> isSpace c |
|
63 |
|| c `elem` "()")) |
|
64 |
wspace = AC.many1 A.space |
|
65 |
rparen = A.skipSpace *> A.char ')' |
|
66 |
finalP = listConfigP <* rparen |
|
67 |
<|> innerDoubleP <* rparen |
|
68 |
<|> stringP <* rparen |
|
69 |
middleP = listConfigP <* wspace |
|
70 |
<|> innerDoubleP <* wspace |
|
71 |
<|> stringP <* wspace |
|
72 |
|
|
73 |
|
|
74 |
-- | Find a configuration having the given string as its first element, |
|
75 |
-- from a list of configurations. |
|
76 |
findConf :: String -> [LispConfig] -> Result LispConfig |
|
77 |
findConf key configs = |
|
78 |
case find (isNamed key) configs of |
|
79 |
(Just c) -> Ok c |
|
80 |
_ -> Bad "Configuration not found" |
|
81 |
|
|
82 |
-- | Get the value of of a configuration having the given string as its |
|
83 |
-- first element. |
|
84 |
-- The value is the content of the configuration, discarding the name itself. |
|
85 |
getValue :: (FromLispConfig a) => String -> [LispConfig] -> Result a |
|
86 |
getValue key configs = findConf key configs >>= fromLispConfig |
|
87 |
|
|
88 |
-- | Extract the values of a configuration containing a list of them. |
|
89 |
extractValues :: LispConfig -> Result [LispConfig] |
|
90 |
extractValues c = tail `fmap` fromLispConfig c |
|
91 |
|
|
92 |
-- | Verify whether the given configuration has a certain name or not.fmap |
|
93 |
-- The name of a configuration is its first parameter, if it is a string. |
|
94 |
isNamed :: String -> LispConfig -> Bool |
|
95 |
isNamed key (LCList (LCString x:_)) = x == key |
|
96 |
isNamed _ _ = False |
|
97 |
|
|
98 |
-- | Parser for recognising the current state of a Xen domain. |
|
99 |
parseState :: String -> ActualState |
|
100 |
parseState s = |
|
101 |
case s of |
|
102 |
"r-----" -> ActualRunning |
|
103 |
"-b----" -> ActualBlocked |
|
104 |
"--p---" -> ActualPaused |
|
105 |
"---s--" -> ActualShutdown |
|
106 |
"----c-" -> ActualCrashed |
|
107 |
"-----d" -> ActualDying |
|
108 |
_ -> ActualUnknown |
|
109 |
|
|
110 |
-- | Extract the configuration data of a Xen domain from a generic LispConfig |
|
111 |
-- data structure. Fail if the LispConfig does not represent a domain. |
|
112 |
getDomainConfig :: LispConfig -> Result Domain |
|
113 |
getDomainConfig configData = do |
|
114 |
domainConf <- |
|
115 |
if isNamed "domain" configData |
|
116 |
then extractValues configData |
|
117 |
else Bad $ "Not a domain configuration: " ++ show configData |
|
118 |
domid <- getValue "domid" domainConf |
|
119 |
name <- getValue "name" domainConf |
|
120 |
cpuTime <- getValue "cpu_time" domainConf |
|
121 |
state <- getValue "state" domainConf |
|
122 |
let actualState = parseState state |
|
123 |
return $ Domain domid name cpuTime actualState Nothing |
|
124 |
|
|
125 |
-- | A parser for parsing the output of the @xm list --long@ command. |
|
126 |
-- It adds the semantic layer on top of lispConfigParser. |
|
127 |
-- It returns a map of domains, with their name as the key. |
|
128 |
-- FIXME: This is efficient under the assumption that only a few fields of the |
|
129 |
-- domain configuration are actually needed. If many of them are required, a |
|
130 |
-- parser able to directly extract the domain config would actually be better. |
|
131 |
xmListParser :: Parser (Map.Map String Domain) |
|
132 |
xmListParser = do |
|
133 |
configs <- lispConfigParser `AC.manyTill` A.endOfInput |
|
134 |
let domains = map getDomainConfig configs |
|
135 |
foldResult m (Ok val) = Ok $ Map.insert (domName val) val m |
|
136 |
foldResult _ (Bad msg) = Bad msg |
|
137 |
case foldM foldResult Map.empty domains of |
|
138 |
Ok d -> return d |
|
139 |
Bad msg -> fail msg |
b/test/data/xen-xm-list-long-4.0.1.txt | ||
---|---|---|
1 |
(domain |
|
2 |
(domid 0) |
|
3 |
(cpu_weight 2048) |
|
4 |
(cpu_cap 0) |
|
5 |
(bootloader ) |
|
6 |
(on_crash restart) |
|
7 |
(uuid 00000000-0000-0000-0000-000000000000) |
|
8 |
(bootloader_args ) |
|
9 |
(vcpus 24) |
|
10 |
(name Domain-0) |
|
11 |
(cpus |
|
12 |
((0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23) |
|
13 |
(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23) |
|
14 |
(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23) |
|
15 |
(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23) |
|
16 |
(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23) |
|
17 |
(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23) |
|
18 |
(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23) |
|
19 |
(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23) |
|
20 |
(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23) |
|
21 |
(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23) |
|
22 |
(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23) |
|
23 |
(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23) |
|
24 |
(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23) |
|
25 |
(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23) |
|
26 |
(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23) |
|
27 |
(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23) |
|
28 |
(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23) |
|
29 |
(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23) |
|
30 |
(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23) |
|
31 |
(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23) |
|
32 |
(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23) |
|
33 |
(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23) |
|
34 |
(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23) |
|
35 |
(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23) |
|
36 |
) |
|
37 |
) |
|
38 |
(on_reboot restart) |
|
39 |
(on_poweroff destroy) |
|
40 |
(maxmem 16777215) |
|
41 |
(memory 1023) |
|
42 |
(shadow_memory 0) |
|
43 |
(features ) |
|
44 |
(on_xend_start ignore) |
|
45 |
(on_xend_stop ignore) |
|
46 |
(cpu_time 184000.41332) |
|
47 |
(online_vcpus 1) |
|
48 |
(image (linux (kernel ) (superpages 0) (nomigrate 0) (tsc_mode 0))) |
|
49 |
(status 2) |
|
50 |
(state r-----) |
|
51 |
) |
|
52 |
(domain |
|
53 |
(domid 119) |
|
54 |
(cpu_weight 256) |
|
55 |
(cpu_cap 0) |
|
56 |
(bootloader ) |
|
57 |
(on_crash restart) |
|
58 |
(uuid e430b4b8-dc91-9390-dfe0-b83c138ea0aa) |
|
59 |
(bootloader_args ) |
|
60 |
(vcpus 1) |
|
61 |
(description ) |
|
62 |
(name instance1.example.com) |
|
63 |
(cpus (())) |
|
64 |
(on_reboot restart) |
|
65 |
(on_poweroff destroy) |
|
66 |
(maxmem 128) |
|
67 |
(memory 128) |
|
68 |
(shadow_memory 0) |
|
69 |
(features ) |
|
70 |
(on_xend_start ignore) |
|
71 |
(on_xend_stop ignore) |
|
72 |
(start_time 1357749308.05) |
|
73 |
(cpu_time 24.116146647) |
|
74 |
(online_vcpus 1) |
|
75 |
(image |
|
76 |
(linux |
|
77 |
(kernel /boot/vmlinuz-ganetixenu) |
|
78 |
(args 'root=/dev/xvda1 ro') |
|
79 |
(superpages 0) |
|
80 |
(videoram 4) |
|
81 |
(pci ()) |
|
82 |
(nomigrate 0) |
|
83 |
(tsc_mode 0) |
|
84 |
(notes |
|
85 |
(HV_START_LOW 18446603336221196288) |
|
86 |
(FEATURES '!writable_page_tables|pae_pgdir_above_4gb') |
|
87 |
(VIRT_BASE 18446744071562067968) |
|
88 |
(GUEST_VERSION 2.6) |
|
89 |
(PADDR_OFFSET 0) |
|
90 |
(GUEST_OS linux) |
|
91 |
(HYPERCALL_PAGE 18446744071578849280) |
|
92 |
(LOADER generic) |
|
93 |
(SUSPEND_CANCEL 1) |
|
94 |
(PAE_MODE yes) |
|
95 |
(ENTRY 18446744071592116736) |
|
96 |
(XEN_VERSION xen-3.0) |
|
97 |
) |
|
98 |
) |
|
99 |
) |
|
100 |
(status 2) |
|
101 |
(state -b----) |
|
102 |
(store_mfn 8836555) |
|
103 |
(console_mfn 8735251) |
|
104 |
(device |
|
105 |
(vif |
|
106 |
(bridge xen-br0) |
|
107 |
(mac aa:00:00:30:8d:9d) |
|
108 |
(script /etc/xen/scripts/vif-bridge) |
|
109 |
(uuid f57c4758-cf0a-8227-6d13-fe26ece82d75) |
|
110 |
(backend 0) |
|
111 |
) |
|
112 |
) |
|
113 |
(device |
|
114 |
(console |
|
115 |
(protocol vt100) |
|
116 |
(location 2) |
|
117 |
(uuid 7695737a-ffc2-4e0d-7f6d-734143b8afc4) |
|
118 |
) |
|
119 |
) |
|
120 |
(device |
|
121 |
(vbd |
|
122 |
(protocol x86_64-abi) |
|
123 |
(uuid 409e1ff8-435a-4704-80bb-4bfe800d932e) |
|
124 |
(bootable 1) |
|
125 |
(dev sda:disk) |
|
126 |
(uname |
|
127 |
phy:/var/run/ganeti/instance-disks/instance1.example.com:0 |
|
128 |
) |
|
129 |
(mode w) |
|
130 |
(backend 0) |
|
131 |
(VDI ) |
|
132 |
) |
|
133 |
) |
|
134 |
) |
b/test/hs/Test/Ganeti/Hypervisor/Xen/XmParser.hs | ||
---|---|---|
1 |
{-# LANGUAGE TemplateHaskell #-} |
|
2 |
{-# OPTIONS_GHC -fno-warn-orphans #-} |
|
3 |
|
|
4 |
{-| Unittests for @xm list --long@ parser -} |
|
5 |
|
|
6 |
{- |
|
7 |
|
|
8 |
Copyright (C) 2013 Google Inc. |
|
9 |
|
|
10 |
This program is free software; you can redistribute it and/or modify |
|
11 |
it under the terms of the GNU General Public License as published by |
|
12 |
the Free Software Foundation; either version 2 of the License, or |
|
13 |
(at your option) any later version. |
|
14 |
|
|
15 |
This program is distributed in the hope that it will be useful, but |
|
16 |
WITHOUT ANY WARRANTY; without even the implied warranty of |
|
17 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
18 |
General Public License for more details. |
|
19 |
|
|
20 |
You should have received a copy of the GNU General Public License |
|
21 |
along with this program; if not, write to the Free Software |
|
22 |
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
|
23 |
02110-1301, USA. |
|
24 |
|
|
25 |
-} |
|
26 |
|
|
27 |
module Test.Ganeti.Hypervisor.Xen.XmParser |
|
28 |
( testHypervisor_Xen_XmParser |
|
29 |
) where |
|
30 |
|
|
31 |
import Test.HUnit |
|
32 |
import Test.QuickCheck as QuickCheck hiding (Result) |
|
33 |
|
|
34 |
import Test.Ganeti.TestHelper |
|
35 |
import Test.Ganeti.TestCommon |
|
36 |
|
|
37 |
import Control.Monad (liftM) |
|
38 |
import qualified Data.Attoparsec.Text as A |
|
39 |
import Data.Text (pack) |
|
40 |
import Data.Char |
|
41 |
import qualified Data.Map as Map |
|
42 |
|
|
43 |
import Ganeti.Hypervisor.Xen.Types |
|
44 |
import Ganeti.Hypervisor.Xen.XmParser |
|
45 |
|
|
46 |
{-# ANN module "HLint: ignore Use camelCase" #-} |
|
47 |
|
|
48 |
-- * Arbitraries |
|
49 |
|
|
50 |
-- | Arbitrary instance for generating configurations. |
|
51 |
-- A completely arbitrary configuration would contain too many lists and its |
|
52 |
-- size would be to big to be actually parsable in reasonable time. |
|
53 |
-- This Arbitrary builds a random Config that is still of a reasonable size. |
|
54 |
-- Avoid generating strings that might be interpreted as numbers. |
|
55 |
instance Arbitrary LispConfig where |
|
56 |
arbitrary = frequency |
|
57 |
[ (5, liftM LCString (genName `suchThat` (not . canBeNumber))) |
|
58 |
, (5, liftM LCDouble arbitrary) |
|
59 |
, (1, liftM LCList (choose(1,20) >>= (`vectorOf` arbitrary))) |
|
60 |
] |
|
61 |
|
|
62 |
-- | Determines conservatively whether a string could be a number. |
|
63 |
canBeNumber :: String -> Bool |
|
64 |
canBeNumber [] = False |
|
65 |
canBeNumber (c:[]) = canBeNumberChar c |
|
66 |
canBeNumber (c:xs) = canBeNumberChar c && canBeNumber xs |
|
67 |
|
|
68 |
-- | Determines whether a char can be part of the string representation of a |
|
69 |
-- number (even in scientific notation). |
|
70 |
canBeNumberChar :: Char -> Bool |
|
71 |
canBeNumberChar c = isDigit c || (c `elem` "eE-") |
|
72 |
|
|
73 |
-- * Helper functions for tests |
|
74 |
|
|
75 |
-- | Function for testing whether a domain configuration is parsed correctly. |
|
76 |
testDomain :: String -> Map.Map String Domain -> Assertion |
|
77 |
testDomain fileName expectedContent = do |
|
78 |
fileContent <- readTestData fileName |
|
79 |
case A.parseOnly xmListParser $ pack fileContent of |
|
80 |
Left msg -> assertFailure $ "Parsing failed: " ++ msg |
|
81 |
Right obtained -> assertEqual fileName expectedContent obtained |
|
82 |
|
|
83 |
-- | Determines whether two LispConfig are equal, with the exception of Double |
|
84 |
-- values, that just need to be "almost equal". |
|
85 |
-- Meant mainly for testing purposes, given that Double values may be slightly |
|
86 |
-- rounded during parsing. |
|
87 |
isAlmostEqual :: LispConfig -> LispConfig -> Bool |
|
88 |
isAlmostEqual (LCList c1) (LCList c2) = |
|
89 |
(length c1 == length c2) && |
|
90 |
foldr |
|
91 |
(\current acc -> (acc && uncurry isAlmostEqual current)) |
|
92 |
True |
|
93 |
(zip c1 c2) |
|
94 |
isAlmostEqual (LCString s1) (LCString s2) = s1 == s2 |
|
95 |
isAlmostEqual (LCDouble d1) (LCDouble d2) = abs (d1-d2) <= 1e-12 |
|
96 |
isAlmostEqual _ _ = False |
|
97 |
|
|
98 |
-- | Function to serialize LispConfigs in such a way that they can be rebuilt |
|
99 |
-- again by the lispConfigParser. |
|
100 |
serializeConf :: LispConfig -> String |
|
101 |
serializeConf (LCList c) = "(" ++ unwords (map serializeConf c) ++ ")" |
|
102 |
serializeConf (LCString s) = s |
|
103 |
serializeConf (LCDouble d) = show d |
|
104 |
|
|
105 |
-- | Test whether a randomly generated config can be parsed. |
|
106 |
-- Implicitly, this also tests that the Show instance of Config is correct. |
|
107 |
prop_config :: LispConfig -> Property |
|
108 |
prop_config conf = |
|
109 |
case A.parseOnly lispConfigParser . pack . serializeConf $ conf of |
|
110 |
Left msg -> fail $ "Parsing failed: " ++ msg |
|
111 |
Right obtained -> property $ isAlmostEqual obtained conf |
|
112 |
|
|
113 |
-- | Test a Xen 4.0.1 @xm list --long@ output. |
|
114 |
case_xen401list :: Assertion |
|
115 |
case_xen401list = testDomain "xen-xm-list-long-4.0.1.txt" $ |
|
116 |
Map.fromList |
|
117 |
[ ("Domain-0", Domain 0 "Domain-0" 184000.41332 ActualRunning Nothing) |
|
118 |
, ("instance1.example.com", Domain 119 "instance1.example.com" 24.116146647 |
|
119 |
ActualBlocked Nothing) |
|
120 |
] |
|
121 |
|
|
122 |
testSuite "Hypervisor/Xen/XmParser" |
|
123 |
[ 'prop_config |
|
124 |
, 'case_xen401list |
|
125 |
] |
b/test/hs/htest.hs | ||
---|---|---|
51 | 51 |
import Test.Ganeti.HTools.Node |
52 | 52 |
import Test.Ganeti.HTools.PeerMap |
53 | 53 |
import Test.Ganeti.HTools.Types |
54 |
import Test.Ganeti.Hypervisor.Xen.XmParser |
|
54 | 55 |
import Test.Ganeti.JSON |
55 | 56 |
import Test.Ganeti.Jobs |
56 | 57 |
import Test.Ganeti.JQueue |
... | ... | |
103 | 104 |
, testHTools_Node |
104 | 105 |
, testHTools_PeerMap |
105 | 106 |
, testHTools_Types |
107 |
, testHypervisor_Xen_XmParser |
|
106 | 108 |
, testJSON |
107 | 109 |
, testJobs |
108 | 110 |
, testJQueue |
Also available in: Unified diff