In order to fetch information about the uptime of the VMs running in Xen,
we need to analyze the output of the "xm uptime" command.

This commit adds the parser to do that, and its tests.

Signed-off-by: Michele Tartara <[email protected]>
---
 Makefile.am                                        |  3 +
 src/Ganeti/Hypervisor/Xen/Types.hs                 | 13 ++++
 src/Ganeti/Hypervisor/Xen/XmUptimeParser.hs        | 52 +++++++++++++
 test/data/xen-xm-uptime-3.0.txt                    |  3 +
 .../Test/Ganeti/Hypervisor/Xen/XmUptimeParser.hs   | 88 ++++++++++++++++++++++
 test/hs/htest.hs                                   |  2 +
 6 files changed, 161 insertions(+)
 create mode 100644 src/Ganeti/Hypervisor/Xen/XmUptimeParser.hs
 create mode 100644 test/data/xen-xm-uptime-3.0.txt
 create mode 100644 test/hs/Test/Ganeti/Hypervisor/Xen/XmUptimeParser.hs

diff --git a/Makefile.am b/Makefile.am
index 9af1f87..f98802a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -521,6 +521,7 @@ HS_LIB_SRCS = \
        src/Ganeti/HTools/Program/Main.hs \
        src/Ganeti/HTools/Types.hs \
        src/Ganeti/Hypervisor/Xen/XmListParser.hs \
+       src/Ganeti/Hypervisor/Xen/XmUptimeParser.hs \
        src/Ganeti/Hypervisor/Xen/Types.hs \
        src/Ganeti/Hash.hs \
        src/Ganeti/JQueue.hs \
@@ -572,6 +573,7 @@ HS_TEST_SRCS = \
        test/hs/Test/Ganeti/HTools/PeerMap.hs \
        test/hs/Test/Ganeti/HTools/Types.hs \
        test/hs/Test/Ganeti/Hypervisor/Xen/XmListParser.hs \
+       test/hs/Test/Ganeti/Hypervisor/Xen/XmUptimeParser.hs \
        test/hs/Test/Ganeti/JSON.hs \
        test/hs/Test/Ganeti/Jobs.hs \
        test/hs/Test/Ganeti/JQueue.hs \
@@ -1053,6 +1055,7 @@ TEST_FILES = \
        test/data/xen-xm-list-4.0.1-dom0-only.txt \
        test/data/xen-xm-list-4.0.1-four-instances.txt \
        test/data/xen-xm-list-long-3.0.txt \
+       test/data/xen-xm-uptime-3.0.txt \
        test/py/ganeti-cli.test \
        test/py/gnt-cli.test \
        test/py/import-export_unittest-helper
diff --git a/src/Ganeti/Hypervisor/Xen/Types.hs 
b/src/Ganeti/Hypervisor/Xen/Types.hs
index 3b10b30..b2e5745 100644
--- a/src/Ganeti/Hypervisor/Xen/Types.hs
+++ b/src/Ganeti/Hypervisor/Xen/Types.hs
@@ -27,8 +27,11 @@ module Ganeti.Hypervisor.Xen.Types
   , Domain(..)
   , FromConfig(..)
   , isAlmostEqual
+  , UptimeInfo(..)
   ) where
 
+import Text.Printf
+
 import Ganeti.BasicTypes
 import Ganeti.Objects
 
@@ -104,3 +107,13 @@ instance FromConfig [Config] where
   fromConfig c =
     Bad $ "Unable to extract a List from this configuration: "
       ++ show c
+
+-- Data type representing the information that can be obtained from "xm uptime"
+data UptimeInfo = UptimeInfo
+  { uInfoName :: String
+  , uInfoID :: Int
+  , uInfoUptime :: String
+  } deriving (Eq)
+
+instance Show UptimeInfo where
+  show (UptimeInfo name idNum uptime) = printf "%s\t%d\t%s" name idNum uptime
diff --git a/src/Ganeti/Hypervisor/Xen/XmUptimeParser.hs 
b/src/Ganeti/Hypervisor/Xen/XmUptimeParser.hs
new file mode 100644
index 0000000..bf2e8f7
--- /dev/null
+++ b/src/Ganeti/Hypervisor/Xen/XmUptimeParser.hs
@@ -0,0 +1,52 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-| Parser for the output of the "xm uptime" command of Xen
+
+-}
+{-
+
+Copyright (C) 2013 Google Inc.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA.
+
+-}
+module Ganeti.Hypervisor.Xen.XmUptimeParser (xmUptimeParser, uptimeLineParser) 
where
+
+import Control.Applicative ((<*))
+import qualified Data.Attoparsec.Text as A
+import qualified Data.Attoparsec.Combinator as AC
+import Data.Attoparsec.Text (Parser)
+import Data.Char (isSpace)
+import qualified Data.Map as Map
+import Data.Text (unpack)
+
+import Ganeti.Hypervisor.Xen.Types
+
+-- | A parser for parsing the output of the "xm uptime" command.
+xmUptimeParser :: Parser (Map.Map Int UptimeInfo)
+xmUptimeParser = do
+  _ <- headerParser
+  uptimes <- uptimeLineParser `AC.manyTill` A.endOfInput
+  return $ Map.fromList [(uInfoID u, u) | u <- uptimes]
+    where headerParser = A.string "Name" <* A.skipSpace <* A.string "ID"
+            <* A.skipSpace <* A.string "Uptime" <* A.skipSpace
+
+-- | A helper for parsing a single line of the "xm uptime" output
+uptimeLineParser :: Parser UptimeInfo
+uptimeLineParser = do
+  name <- A.takeTill isSpace <* A.skipSpace
+  idNum <- A.decimal <* A.skipSpace
+  uptime <- A.takeTill A.isEndOfLine <* A.skipSpace
+  return . UptimeInfo (unpack name) idNum $ unpack uptime
diff --git a/test/data/xen-xm-uptime-3.0.txt b/test/data/xen-xm-uptime-3.0.txt
new file mode 100644
index 0000000..8e1d5b6
--- /dev/null
+++ b/test/data/xen-xm-uptime-3.0.txt
@@ -0,0 +1,3 @@
+Name                                ID Uptime
+Domain-0                             0 98 days,  2:27:44
+instance1.example.com               119 15 days, 20:57:07
diff --git a/test/hs/Test/Ganeti/Hypervisor/Xen/XmUptimeParser.hs 
b/test/hs/Test/Ganeti/Hypervisor/Xen/XmUptimeParser.hs
new file mode 100644
index 0000000..71e5aad
--- /dev/null
+++ b/test/hs/Test/Ganeti/Hypervisor/Xen/XmUptimeParser.hs
@@ -0,0 +1,88 @@
+{-# LANGUAGE TemplateHaskell #-}
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+
+{-| Unittests for "xm list --long" parser -}
+
+{-
+
+Copyright (C) 2013 Google Inc.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA.
+
+-}
+
+module Test.Ganeti.Hypervisor.Xen.XmUptimeParser 
(testHypervisor_Xen_XmUptimeParser) where
+
+import Test.HUnit
+import Test.QuickCheck as QuickCheck hiding (Result)
+
+import Test.Ganeti.TestHelper
+import Test.Ganeti.TestCommon
+
+import qualified Data.Attoparsec.Text as A
+import qualified Data.Map as Map
+import Data.Text (pack)
+import Text.Printf
+
+import Ganeti.Hypervisor.Xen.Types
+import Ganeti.Hypervisor.Xen.XmUptimeParser
+
+{-# ANN module "HLint: ignore Use camelCase" #-}
+
+-- | Generates an arbitrary "xm uptime" output
+instance Arbitrary UptimeInfo where
+  arbitrary = do
+    name <- genFQDN
+    idNum <- (arbitrary :: Gen Int) `suchThat` (>= 0)
+    days <- (arbitrary :: Gen Int) `suchThat` (>= 0)
+    hours <- choose (0, 23) :: Gen Int
+    mins <- choose (0, 59) :: Gen Int
+    secs <- choose (0, 59) :: Gen Int
+    let uptime :: String
+        uptime =
+          if days == 0
+            then printf "%d days, %d:%d:%d" days hours mins secs
+            else printf "%d:%d:%d" hours mins secs
+    return $ UptimeInfo name idNum uptime
+
+-- | Test whether a randomly generated UptimeInfo text line can be parsed.
+prop_UptimeInfo :: UptimeInfo -> Property
+prop_UptimeInfo uInfo =
+  case A.parseOnly uptimeLineParser . pack . show $ uInfo of
+    Left msg -> fail $ "Parsing failed: " ++ msg
+    Right obtained -> obtained ==? uInfo
+
+-- | Function for testing whether a "xm uptime" output (stored in a file)
+-- is parsed correctly.
+testUptimeInfo :: String -> Map.Map Int UptimeInfo -> Assertion
+testUptimeInfo fileName expectedContent = do
+    fileContent <- readTestData fileName
+    case A.parseOnly xmUptimeParser $ pack fileContent of
+        Left msg -> assertFailure $ "Parsing failed: " ++ msg
+        Right obtained -> assertEqual fileName expectedContent obtained
+
+-- Test a Xen 3.0 "xm list --long" output
+case_xen30 :: Assertion
+case_xen30 = testUptimeInfo "xen-xm-uptime-3.0.txt" $
+  Map.fromList
+    [ (0, UptimeInfo "Domain-0" 0 "98 days,  2:27:44")
+    , (119, UptimeInfo "instance1.example.com" 119 "15 days, 20:57:07")
+    ]
+
+testSuite "Hypervisor/Xen/XmUptimeParser"
+          [ 'prop_UptimeInfo
+          , 'case_xen30
+          ]
diff --git a/test/hs/htest.hs b/test/hs/htest.hs
index 222eca8..89d6a9f 100644
--- a/test/hs/htest.hs
+++ b/test/hs/htest.hs
@@ -52,6 +52,7 @@ import Test.Ganeti.HTools.Node
 import Test.Ganeti.HTools.PeerMap
 import Test.Ganeti.HTools.Types
 import Test.Ganeti.Hypervisor.Xen.XmListParser
+import Test.Ganeti.Hypervisor.Xen.XmUptimeParser
 import Test.Ganeti.JSON
 import Test.Ganeti.Jobs
 import Test.Ganeti.JQueue
@@ -104,6 +105,7 @@ allTests =
   , testHTools_PeerMap
   , testHTools_Types
   , testHypervisor_Xen_XmListParser
+  , testHypervisor_Xen_XmUptimeParser
   , testJSON
   , testJobs
   , testJQueue
-- 
1.8.1

Reply via email to