nmis-parser (empty) → 0.1.0.0
raw patch · 11 files changed
+558/−0 lines, 11 filesdep +Nmisdep +basedep +containersbinary-added
Dependencies added: Nmis, base, containers, megaparsec
Files
- .DS_Store binary
- ._.DS_Store binary
- LICENSE +30/−0
- README.md +29/−0
- nmis-parser.cabal +40/−0
- src/Text/Internal/Helper.hs +93/−0
- src/Text/Internal/Helper.hs~ +84/−0
- src/Text/Internal/NmisTypes.hs +44/−0
- src/Text/Internal/NmisTypes.hs~ +36/−0
- src/Text/Nmis.hs +200/−0
- test/Spec.hs +2/−0
+ .DS_Store view
binary file changed (absent → 6148 bytes)
+ ._.DS_Store view
binary file changed (absent → 120 bytes)
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Author name here (c) 2017++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Author name here nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,29 @@+# NMIS file parser + - NMIS stands for Network Management Information System++This parser parses the NMIS format files to `Nmis` record type++#### Example usage :++```haskell+ module Main where+ import System.Environment (getArgs)+ import System.IO+ import Text.Megaparsec+ import Text.Nmis++ main :: IO ()+ main = do+ args <- getArgs+ case args of+ [] -> putStrLn "error: you need to pass in file path"+ [path] -> do+ contents <- readFile path+ let result = parse parseNmis "" contents+ case result of+ Left nodes -> print $ parseErrorPretty nodes+ Right nodes -> do+ print nodes+ _ -> putStrLn "error: you need to pass in only one file path"++```
+ nmis-parser.cabal view
@@ -0,0 +1,40 @@+name: nmis-parser+version: 0.1.0.0+synopsis: NMIS file parser+homepage: https://github.com/v0d1ch/nmis-parser#readme+license: BSD3+license-file: LICENSE+author: Sasa Bogicevic+maintainer: brutallesale@gmail.com+copyright: 2017 Sasa Bogicevic+category: Text, Parsers+build-type: Simple+extra-source-files: README.md+cabal-version: >=1.10++description:+ Parser for __NMIS__ (Network Management Information System) files to record type. Main module to use is __Text.Nmis__ and __parseNmis__ function++library+ hs-source-dirs: src+ exposed-modules: Text.Nmis+ , Text.Internal.Helper+ , Text.Internal.NmisTypes+ build-depends: base >= 4.7 && < 5+ , containers+ , megaparsec+ default-language: Haskell2010+++test-suite nmis-test+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ main-is: Spec.hs+ build-depends: base+ , Nmis+ ghc-options: -threaded -rtsopts -with-rtsopts=-N+ default-language: Haskell2010++source-repository head+ type: git+ location: https://github.com/v0d1ch/nmis-parser
+ src/Text/Internal/Helper.hs view
@@ -0,0 +1,93 @@+{-|+Module : Text.Internal.Helper+Description : Helper for parse actions +Copyright : (c) Sasa Bogicevic, 2017+License : GPL-3+Maintainer : brutallesale@gmail.com+Stability : experimental+Uses megaparsec library +-}++{-# LANGUAGE OverloadedStrings #-}++module Text.Internal.Helper where++import Control.Applicative (empty)+import Control.Monad (void)+import Prelude hiding (until)+import Text.Megaparsec+import qualified Text.Megaparsec.Lexer as L+import Text.Megaparsec.String++-- | space consumer - consume space and comments+spaceConsumer :: Parser ()+spaceConsumer = L.space (void spaceChar) lineCmnt blockCmnt+ where+ lineCmnt = L.skipLineComment "#"+ blockCmnt = L.skipBlockComment "/*" "*/"+-- | hashbang line comment+lineComment :: Parser ()+lineComment = L.skipLineComment "#"++-- | space consumer no 2+scn :: Parser ()+scn = L.space (void spaceChar) lineComment empty++-- | lexeme+lexeme :: Parser a -> Parser a+lexeme = L.lexeme spaceConsumer++-- | symbol+symbol :: String -> Parser String+symbol = L.symbol scn++-- | arrow - parse haskell constraint like arrow sign+parrow :: Parser String+parrow = lexeme $ string " => "++-- | equals - parse equals sign+pequals :: Parser String+pequals = lexeme $ string " = "++-- | parse all between parens+parens :: Parser a -> Parser a+parens = between (string "(") (string ")")++-- | parse all between braces+braces :: Parser a -> Parser a+braces = between (string "{") (string "}")++-- | parse until string passed as function parameter+until :: String -> Parsec Dec String String+until s = anyChar `manyTill` string s++-- | parse until newline character+untilEol :: Parsec Dec String String+untilEol = anyChar `someTill` newline++-- | parse until newline character+phash :: Parser String+phash = lexeme $ string' "%hash" >> pequals++-- | parse quoted string with optional space in front of it+pQuotedStr :: Parser String+pQuotedStr = do+ _ <- optional space+ string' "'" >> until "'"++-- | parse 'undef' literal+pUndefined :: Parser String+pUndefined = do+ _ <- optional space+ string' "undef"++-- | combined parser for single record+pOpt :: Parser (String, String)+pOpt = do+ _ <- space+ key <- pQuotedStr+ _ <- space >> string "=>" >> space+ value <- try (pQuotedStr <|> pUndefined <|> many numberChar)+ _ <- optional $ string' ","+ _ <- newline+ return (key, value)
+ src/Text/Internal/Helper.hs~ view
@@ -0,0 +1,84 @@+{-# LANGUAGE OverloadedStrings #-}+-- | This helper module contains functions to parse nmis file.+-- It uses megapasec library+module Helper where++import Control.Applicative (empty)+import Control.Monad (void)+import Prelude hiding (until)+import Text.Megaparsec+import qualified Text.Megaparsec.Lexer as L+import Text.Megaparsec.String++-- | space consumer - consume space and comments+spaceConsumer :: Parser ()+spaceConsumer = L.space (void spaceChar) lineCmnt blockCmnt+ where+ lineCmnt = L.skipLineComment "#"+ blockCmnt = L.skipBlockComment "/*" "*/"+-- | hashbang line comment+lineComment :: Parser ()+lineComment = L.skipLineComment "#"++-- | space consumer no 2+scn :: Parser ()+scn = L.space (void spaceChar) lineComment empty++-- | lexeme+lexeme :: Parser a -> Parser a+lexeme = L.lexeme spaceConsumer++-- | symbol+symbol :: String -> Parser String+symbol = L.symbol scn++-- | arrow - parse haskell constraint like arrow sign+parrow :: Parser String+parrow = lexeme $ string " => "++-- | equals - parse equals sign+pequals :: Parser String+pequals = lexeme $ string " = "++-- | parse all between parens+parens :: Parser a -> Parser a+parens = between (string "(") (string ")")++-- | parse all between braces+braces :: Parser a -> Parser a+braces = between (string "{") (string "}")++-- | parse until string passed as function parameter+until :: String -> Parsec Dec String String+until s = anyChar `manyTill` string s++-- | parse until newline character+untilEol :: Parsec Dec String String+untilEol = anyChar `someTill` newline++-- | parse until newline character+phash :: Parser String+phash = lexeme $ string' "%hash" >> pequals++-- | parse quoted string with optional space in front of it+pQuotedStr :: Parser String+pQuotedStr = do+ _ <- optional space+ string' "'" >> until "'"++-- | parse 'undef' literal+pUndefined :: Parser String+pUndefined = do+ _ <- optional space+ string' "undef"++-- | combined parser for single record+pOpt :: Parser (String, String)+pOpt = do+ _ <- space+ key <- pQuotedStr+ _ <- space >> string "=>" >> space+ value <- try (pQuotedStr <|> pUndefined <|> many numberChar)+ _ <- optional $ string' ","+ _ <- newline+ return (key, value)
+ src/Text/Internal/NmisTypes.hs view
@@ -0,0 +1,44 @@+{-|+Module : Text.Internal.NmisTypes+Description : Contains single type that holds all parsed data +Copyright : (c) Sasa Bogicevic, 2017+License : GPL-3+Maintainer : brutallesale@gmail.com+Stability : experimental+-}+module Text.Internal.NmisTypes where++-- | Main type that holds all parsed data+data Nmis = Nmis+ { customer :: Maybe String+ , groups :: Maybe String+ , active :: Maybe String+ , advancedOptions :: Maybe String+ , authKey :: Maybe String+ , authPassword :: Maybe String+ , authProtocol :: Maybe String+ , businessService :: Maybe String+ , calls :: Maybe String+ , cbqos :: Maybe String+ , collect :: Maybe String+ , community :: Maybe String+ , depend :: Maybe String+ , display_name :: Maybe String+ , group :: Maybe String+ , host :: Maybe String+ , location :: Maybe String+ , model :: Maybe String+ , name :: Maybe String+ , netType :: Maybe String+ , ping :: Maybe String+ , port :: Maybe String+ , rancid :: Maybe String+ , roleType :: Maybe String+ , serviceStatus :: Maybe String+ , services :: Maybe String+ , threshold :: Maybe String+ , timezone :: Maybe Integer+ , uuid :: Maybe String+ , version :: Maybe String+ , webserver :: Maybe String+ } deriving (Show)
+ src/Text/Internal/NmisTypes.hs~ view
@@ -0,0 +1,36 @@+module NmisTypes where++-- | Main type that containes all parsed data+data Nmis = Nmis+ { customer :: Maybe String+ , groups :: Maybe String+ , active :: Maybe String+ , advancedOptions :: Maybe String+ , authKey :: Maybe String+ , authPassword :: Maybe String+ , authProtocol :: Maybe String+ , businessService :: Maybe String+ , calls :: Maybe String+ , cbqos :: Maybe String+ , collect :: Maybe String+ , community :: Maybe String+ , depend :: Maybe String+ , display_name :: Maybe String+ , group :: Maybe String+ , host :: Maybe String+ , location :: Maybe String+ , model :: Maybe String+ , name :: Maybe String+ , netType :: Maybe String+ , ping :: Maybe String+ , port :: Maybe String+ , rancid :: Maybe String+ , roleType :: Maybe String+ , serviceStatus :: Maybe String+ , services :: Maybe String+ , threshold :: Maybe String+ , timezone :: Maybe Integer+ , uuid :: Maybe String+ , version :: Maybe String+ , webserver :: Maybe String+ } deriving (Show)
+ src/Text/Nmis.hs view
@@ -0,0 +1,200 @@+{-|+Module : Text.Nmis+Description : Main module for nmis parsing+Copyright : (c) Sasa Bogicevic, 2017+License : GPL-3+Maintainer : brutallesale@gmail.com+Stability : experimental++-}++-- NMIS stands for Network Management Information System+--+-- This parser parses the nmis format files to Nmis record type+--+-- Example usage :+--+-- module Main where+--+-- import System.Environment (getArgs)+--+-- import System.IO+--+-- import Text.Megaparsec+--+-- import Text.Nmis+--+-- main :: IO ()+--+-- main = do+--+-- args <- getArgs+--+-- case args of+--+-- [] -> putStrLn "error: you need to pass in file path"+--+-- [path] -> do+--+-- contents <- readFile path+--+-- let result = parse parseNmis "" contents+--+-- case result of+--+-- Left nodes -> print $ parseErrorPretty nodes+--+-- Right nodes -> do+--+-- print nodes+--+-- _ -> putStrLn "error: you need to pass in only one file path"+--++{-# LANGUAGE OverloadedStrings #-}++module Text.Nmis where+++import Control.Monad (join)+import qualified Data.Map.Strict as M+import Prelude hiding (until)+import Text.Megaparsec+import Text.Megaparsec.String+import Text.Read (readMaybe)+import Data.Maybe (fromMaybe)+import Text.Internal.NmisTypes+import Text.Internal.Helper as H++-- | Parse nmis file to [Nmis]+--+-- __This is a single function you will need to parse the file__+parseNmis :: Parser [Nmis]+parseNmis =+ lexeme $ do+ _ <- optional $ symbol "#"+ _ <- phash+ _ <- space >> string "("+ parseToList+++-- | Parses single record+parseSingle :: Parser Nmis+parseSingle = do+ _ <- optional H.pQuotedStr+ _ <- until "=>" >> space >> string "{" >> newline+ node <- manyTill pOpt (try $ lookAhead $ optional space *> string "}")+ _ <- optional space >> string "}"+ _ <- optional $ string ","+ let rmap = M.fromList node+ return+ Nmis+ { customer = M.lookup "customer" rmap+ , groups = M.lookup "groups" rmap+ , active = M.lookup "active" rmap+ , businessService = M.lookup "businessService" rmap+ , advancedOptions = M.lookup "advancedOptions" rmap+ , authKey = M.lookup "authKey" rmap+ , authPassword = M.lookup "authPassword" rmap+ , authProtocol = M.lookup "authProtocol" rmap+ , calls = M.lookup "calls" rmap+ , cbqos = M.lookup "cbqos" rmap+ , collect = M.lookup "collect" rmap+ , community = M.lookup "community" rmap+ , depend = M.lookup "depend" rmap+ , display_name = M.lookup "display_name" rmap+ , group = M.lookup "group" rmap+ , host = M.lookup "host" rmap+ , location = M.lookup "location" rmap+ , model = M.lookup "model" rmap+ , name = M.lookup "name" rmap+ , netType = M.lookup "netType" rmap+ , ping = M.lookup "ping" rmap+ , port = M.lookup "port" rmap+ , rancid = M.lookup "rancid" rmap+ , roleType = M.lookup "roleType" rmap+ , serviceStatus = M.lookup "serviceStatus" rmap+ , services = M.lookup "services" rmap+ , threshold = M.lookup "threshold" rmap+ , timezone = join $ readMaybe <$> M.lookup "timezone" rmap+ , uuid = M.lookup "uuid" rmap+ , version = M.lookup "version" rmap+ , webserver = M.lookup "webserver" rmap+ }+++-- |+-- This function is used to match records by group/groups field.+--+-- You have two lists with Nmis types [Nmis] [Nmis]+--+-- and you want to match group field from one to the groups field from the other,+--+-- grab a customer field from first list and attach it to the second list which you can save later.+--+-- This is something of practical use which I needed in the real world.+lookupInList :: [Nmis] -> Nmis -> Nmis+lookupInList cust n = do+ let fcusts = filterByGroup (group n) cust+ if length fcusts > 0 then+ n { customer = customer (head fcusts) }+ else n++-- | Filters Nmis by Nodes group+filterByGroup :: Maybe String -> [Nmis] -> [Nmis]+filterByGroup s cl = filter (\c -> groups c == s) cl++-- | parses many single values until ');'+parseToList :: Parser [Nmis]+parseToList = do+ result <-+ manyTill parseSingle (try $ lookAhead $ optional space >> string ")")+ _ <- optional newline+ _ <- string ")"+ _ <- optional $ string ";"+ return result++-- | Show maybe integer for timezone field+showMaybeInt :: Maybe Integer -> String+showMaybeInt i = case i of+ Nothing -> ""+ Just x -> show x++-- | Show Nmis list+--+-- Use for printing the [Nmis] list+showNmis :: [Nmis] -> String+showNmis [] = ""+showNmis (c:cx) =+ "\n '" ++ fromMaybe "" (name c) ++ "' => {\n"+ ++ " 'customer' => '" ++ fromMaybe "" (customer c) ++ "',\n"+ ++ " 'active' => '" ++ fromMaybe "" (active c) ++ "',\n"+ ++ " 'authkey' => '" ++ fromMaybe "" (authKey c) ++ "',\n"+ ++ " 'authpassword' => '" ++ fromMaybe "" (authPassword c) ++ "',\n"+ ++ " 'authprotocol' => '" ++ fromMaybe "" (authProtocol c) ++ "',\n"+ ++ " 'businesService' => '" ++ fromMaybe "" (businessService c) ++ "',\n"+ ++ " 'calls' => '" ++ fromMaybe "" (calls c) ++ "',\n"+ ++ " 'cbqos' => '" ++ fromMaybe "" (cbqos c) ++ "',\n"+ ++ " 'collect' => '" ++ fromMaybe "" (collect c) ++ "',\n"+ ++ " 'community' => '" ++ fromMaybe "" (community c) ++ "',\n"+ ++ " 'depend' => '" ++ fromMaybe "" (depend c) ++ "',\n"+ ++ " 'display_name' => '" ++ fromMaybe "" (display_name c) ++ "',\n"+ ++ " 'group' => '" ++ fromMaybe "" (group c) ++ "',\n"+ ++ " 'host' => '" ++ fromMaybe "" (host c) ++ "',\n"+ ++ " 'location' => '" ++ fromMaybe "" (location c) ++ "',\n"+ ++ " 'model' => '" ++ fromMaybe "" (model c) ++ "',\n"+ ++ " 'name' => '" ++ fromMaybe "" (name c) ++ "',\n"+ ++ " 'netType' => '" ++ fromMaybe "" (netType c) ++ "',\n"+ ++ " 'ping' => '" ++ fromMaybe "" (ping c) ++ "',\n"+ ++ " 'port' => '" ++ fromMaybe "" (port c) ++ "',\n"+ ++ " 'rancid' => '" ++ fromMaybe "" (rancid c) ++ "',\n"+ ++ " 'roleType' => '" ++ fromMaybe "" (roleType c) ++ "',\n"+ ++ " 'serviceStatus' => '" ++ fromMaybe "" (serviceStatus c) ++ "',\n"+ ++ " 'services' => '" ++ fromMaybe "" (services c) ++ "',\n"+ ++ " 'threshold' => '" ++ fromMaybe "" (threshold c) ++ "',\n"+ ++ " 'timezone' => '" ++ showMaybeInt (timezone c) ++ "',\n"+ ++ " 'uuid' => '" ++ fromMaybe "" (uuid c) ++ "',\n"+ ++ " 'version' => '" ++ fromMaybe "" (version c) ++ "',\n"+ ++ " 'webserver' => '" ++ fromMaybe "" (webserver c) ++ "',\n"+ ++ "},"+ ++ showNmis cx
+ test/Spec.hs view
@@ -0,0 +1,2 @@+main :: IO ()+main = putStrLn "Test suite not yet implemented"