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