packages feed

linode-v4 (empty) → 0.1.0.0

raw patch · 8 files changed

+645/−0 lines, 8 filesdep +aesondep +basedep +bytestringsetup-changed

Dependencies added: aeson, base, bytestring, http-conduit, unordered-containers

Files

+ ChangeLog.md view
@@ -0,0 +1,5 @@+# Revision history for linode-v4++## 0.1.0.0  -- YYYY-mm-dd++* First version. Released on an unsuspecting world.
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2016, Phil Eaton++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 Phil Eaton 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 @@+[![Build Status](https://travis-ci.org/eatonphil/linode-haskell.svg?branch=master)](https://travis-ci.org/eatonphil/linode-haskell)++# linode-haskell++This is a Haskell wrapper around the [Linode v4 API](https://developers.linode.com/reference/).+You will need an alpha account and a personal access token. You can get those+at [login.alpha.linode.com](https://login.alpha.linode.com).++## Setup++Install the [Haskell platform](https://www.haskell.org/platform/). You will also need http-conduit:++```bash+$ cabal update+$ cabal install http-conduit+```++## Compile and run++```+$ make+$ LINODE_TOKEN=my-token ./linode+```++## API Documentation++Although documentation for the Haskell wrapper does not yet exist,+feel free to look through the alpha API [documentation](https://developers.linode.com/reference/)+in the meantime.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ linode-v4.cabal view
@@ -0,0 +1,25 @@+-- Initial linode-v4.cabal generated by cabal init.  For further +-- documentation, see http://haskell.org/cabal/users-guide/++name:                linode-v4+version:             0.1.0.0+synopsis:            Haskell wrapper for the Linode v4 API+-- description:         +homepage:            https://github.com/eatonphil/linode-haskell+license:             BSD3+license-file:        LICENSE+author:              Phil Eaton+maintainer:          philneaton95@gmail.com+-- copyright:           +category:            Network+build-type:          Simple+extra-source-files:  ChangeLog.md, README.md+cabal-version:       >=1.10++library+  exposed-modules:     Network.Linode.Api, Network.Linode.Request, Network.Linode.Response+  -- other-modules:       +  other-extensions:    OverloadedStrings, DeriveGeneric, DuplicateRecordFields, ScopedTypeVariables+  build-depends:       base >=4.9 && <4.10, aeson >=0.11 && <0.12, bytestring >=0.10 && <0.11, http-conduit >=2.2 && <2.3, unordered-containers >=0.2 && <0.3+  hs-source-dirs:      src+  default-language:    Haskell2010
+ src/Network/Linode/Api.hs view
@@ -0,0 +1,146 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE ScopedTypeVariables #-}++module Network.Linode.Api where++import qualified Network.Linode.Request as Req (Linode,+                                                Disk,+                                                Config,+                                                Config)+import qualified Network.Linode.Response as Rsp (Linode (id),+                                                 Linodes,+                                                 Service,+                                                 Services,+                                                 Datacenter,+                                                 Datacenters,+                                                 Distribution,+                                                 Distributions,+                                                 DNSZone,+                                                 DNSZones,+                                                 Kernel,+                                                 Kernels,+                                                 Disks,+                                                 Disk,+                                                 Configs,+                                                 Config)++import Prelude hiding (id)++import Data.Aeson+import qualified Data.List (intercalate)+import qualified Data.ByteString as S (ByteString)+import qualified Data.ByteString.Char8 (pack)+import qualified Data.Maybe (Maybe (Just, Nothing))+import qualified Network.HTTP.Conduit (parseRequest_)+import qualified Network.HTTP.Simple (Request, httpJSON, addRequestHeader, getResponseBody, setRequestBodyJSON)+import qualified System.Environment (getEnv)+import qualified Text.Printf (printf)++apiRoot :: String+apiRoot = "https://api.alpha.linode.com/v4"++setAuthHeader :: Network.HTTP.Simple.Request -> IO Network.HTTP.Simple.Request+setAuthHeader request = do+  token <- System.Environment.getEnv "LINODE_TOKEN"+  let authorizationValue :: S.ByteString =+        Data.ByteString.Char8.pack+        $ Text.Printf.printf "token %s" token+  return $ Network.HTTP.Simple.addRequestHeader "Authorization" authorizationValue request++getJson :: Data.Aeson.FromJSON a => String -> [String] -> IO (Data.Maybe.Maybe a)+getJson method urlFragments = do+  let endpoint = Data.List.intercalate "/" urlFragments+  let requestString =+        Text.Printf.printf "%s %s%s" method apiRoot endpoint+  request <- setAuthHeader+             $ Network.HTTP.Conduit.parseRequest_ requestString+  response <- Network.HTTP.Simple.httpJSON request+  return $ Network.HTTP.Simple.getResponseBody response++get :: Data.Aeson.FromJSON a => [String] -> IO (Data.Maybe.Maybe a)+get = getJson "GET"++delete :: Data.Aeson.FromJSON a => [String] -> IO (Data.Maybe.Maybe a)+delete = getJson "DELETE"++sendJson :: Data.Aeson.ToJSON a => Data.Aeson.FromJSON b => String -> [String] -> a -> IO (Data.Maybe.Maybe b)+sendJson method urlFragments object = do+  let endpoint = Data.List.intercalate "/" urlFragments+  let requestString =+        Text.Printf.printf "%s %s%s" method apiRoot endpoint+  request <- setAuthHeader+             $ Network.HTTP.Simple.setRequestBodyJSON object+             $ Network.HTTP.Conduit.parseRequest_ requestString+  response <- Network.HTTP.Simple.httpJSON request+  return $ Network.HTTP.Simple.getResponseBody response++post :: Data.Aeson.ToJSON a => Data.Aeson.FromJSON b => [String] -> a -> IO (Data.Maybe.Maybe b)+post = sendJson "POST"++put :: Data.Aeson.ToJSON a => Data.Aeson.FromJSON b => [String] -> a -> IO (Data.Maybe.Maybe b)+put = sendJson "PUT"++getDatacenters :: IO (Data.Maybe.Maybe Rsp.Datacenters)+getDatacenters = get ["/datacenters"]++getDatacenter :: String -> IO (Data.Maybe.Maybe Rsp.Datacenter)+getDatacenter id = get ["/datacenters", id]++getDistributions :: IO (Data.Maybe.Maybe Rsp.Distributions)+getDistributions = get ["/distributions"]++getDistributionsRecommended :: IO (Data.Maybe.Maybe Rsp.Distributions)+getDistributionsRecommended = get ["/distributions/recommended"]++getDistribution :: String -> IO (Data.Maybe.Maybe Rsp.Distribution)+getDistribution id = get ["/distributions", id]++getLinodes :: IO (Data.Maybe.Maybe Rsp.Linodes)+getLinodes = get ["/linodes"]++getLinode :: String -> IO (Data.Maybe.Maybe Rsp.Linode)+getLinode id = get ["/linodes", id]++addLinode :: Req.Linode -> IO (Data.Maybe.Maybe Rsp.Linode)+addLinode linode = post ["/linodes"] linode++editLinode :: Rsp.Linode -> IO (Data.Maybe.Maybe Rsp.Linode)+editLinode linode = put ["/linodes", Rsp.id linode] linode++getDisks :: String -> IO (Data.Maybe.Maybe Rsp.Disks)+getDisks linodeId = get ["/linodes", linodeId, "disks"]++getDisk :: String -> String -> IO (Data.Maybe.Maybe Rsp.Disk)+getDisk linodeId diskId = get ["/linodes", linodeId, "disks", diskId]++addDisk :: String -> Req.Disk -> IO (Data.Maybe.Maybe Rsp.Disk)+addDisk linodeId disk = post ["/linodes", linodeId] disk++getConfigs :: String -> IO (Data.Maybe.Maybe Rsp.Configs)+getConfigs linodeId = get ["/linodes", linodeId, "configs"]++getConfig :: String -> String -> IO (Data.Maybe.Maybe Rsp.Config)+getConfig linodeId configId = get ["/linodes", linodeId, "configs", configId]++addConfig :: String -> Req.Config -> IO (Data.Maybe.Maybe Rsp.Config)+addConfig linodeId config = post ["/linodes", linodeId, "configs"] config++getServices :: IO (Data.Maybe.Maybe Rsp.Services)+getServices = get ["/services"]++getService :: String -> IO (Data.Maybe.Maybe Rsp.Service)+getService id = get ["/services", id]++getDNSZones :: IO (Data.Maybe.Maybe Rsp.DNSZones)+getDNSZones = get ["/dnszones"]++getDNSZone :: String -> IO (Data.Maybe.Maybe Rsp.DNSZone)+getDNSZone id = get ["/dnszones", id]++getKernels :: IO (Data.Maybe.Maybe Rsp.Kernels)+getKernels = get ["/kernels"]++getKernel :: String -> IO (Data.Maybe.Maybe Rsp.Kernel)+getKernel id = get ["/kernels", id]
+ src/Network/Linode/Request.hs view
@@ -0,0 +1,51 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE ScopedTypeVariables #-}++module Network.Linode.Request where++import qualified Network.Linode.Response as Rsp (Filesystem, RunLevel, VirtMode)++import Data.Aeson+import qualified GHC.Generics (Generic)+import qualified Data.Maybe as M (Maybe)+import qualified Data.HashMap.Strict as H (HashMap)++data Linode = Linode { datacenter :: String,+                       service :: String,+                       source :: M.Maybe String,+                       root_pass :: M.Maybe String,+                       label :: M.Maybe String,+                       group :: M.Maybe String,+                       stackscript :: M.Maybe String,+                       stackscript_udf_responses :: M.Maybe (H.HashMap String String) } deriving (Eq, Show, GHC.Generics.Generic)++instance Data.Aeson.FromJSON Linode+instance Data.Aeson.ToJSON Linode++data Disk = Disk { size :: Int,+                   distribution :: M.Maybe String,+                   root_pass :: M.Maybe String,+                   root_ssh_key :: M.Maybe String,+                   label :: String,+                   filesystem :: Rsp.Filesystem,+                   read_only :: M.Maybe Bool,+                   stackscript :: M.Maybe String,+                   stackscript_udf_responses :: M.Maybe (H.HashMap String String) } deriving (Eq, Show, GHC.Generics.Generic)++instance Data.Aeson.FromJSON Disk+instance Data.Aeson.ToJSON Disk++data Config = Config { kernel :: String,+                       label :: String,+                       disks :: [String],+                       comments :: M.Maybe String,+                       ram_limit :: M.Maybe Int,+                       root_device_ro :: M.Maybe Bool,+                       devtmpfs_automount :: M.Maybe Bool,+                       run_level :: Rsp.RunLevel,+                       virt_mode :: Rsp.VirtMode } deriving (Eq, Show, GHC.Generics.Generic)++instance Data.Aeson.FromJSON Config+instance Data.Aeson.ToJSON Config
+ src/Network/Linode/Response.hs view
@@ -0,0 +1,357 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE ScopedTypeVariables #-}++module Network.Linode.Response where++import Data.Aeson+import qualified GHC.Generics (Generic)+import qualified Data.Maybe as M (Maybe)++data Datacenter = Datacenter { id :: String,+                               datacenter :: String,+                               label :: String } deriving (Eq, Show, GHC.Generics.Generic)++instance Data.Aeson.FromJSON Datacenter+instance Data.Aeson.ToJSON Datacenter++data Datacenters = Datacenters { total_pages :: Int,+                                 total_results :: Int,+                                 page :: Int,+                                 datacenters :: [Datacenter] } deriving (Eq, Show, GHC.Generics.Generic)++instance Data.Aeson.FromJSON Datacenters+instance Data.Aeson.ToJSON Datacenters++data Distribution = Distribution { id :: String,+                                   created :: String,+                                   vendor :: String,+                                   recommended :: Bool,+                                   minimum_storage_size :: Int,+                                   x64 :: Bool,+                                   label :: String } deriving (Eq, Show, GHC.Generics.Generic)++instance Data.Aeson.FromJSON Distribution+instance Data.Aeson.ToJSON Distribution++data Distributions = Distributions { total_pages :: Int,+                                     total_results :: Int,+                                     page :: Int,+                                     distributions :: [Distributions] } deriving (Eq, Show, GHC.Generics.Generic)++instance Data.Aeson.FromJSON Distributions+instance Data.Aeson.ToJSON Distributions++data Service = Service { id :: String,+                         storage :: Int,+                         hourly_price :: Int,+                         label :: String,+                         mbits_out :: M.Maybe Int,+                         monthly_price :: Int,+                         ram :: Int,+                         service_type :: String,+                         transfer :: Int,+                         vcpus :: M.Maybe Int } deriving (Eq, Show, GHC.Generics.Generic)+++instance Data.Aeson.FromJSON Service+instance Data.Aeson.ToJSON Service++data Services = Services { total_pages :: Int,+                           total_results :: Int,+                           page :: Int,+                           services :: [Service] } deriving (Eq, Show, GHC.Generics.Generic)++instance Data.Aeson.FromJSON Services+instance Data.Aeson.ToJSON Services+++data DNSZone = DNSZone { id :: String,+                         dnszone :: String,+                         soa_email :: String,+                         description :: String,+                         refresh_sec :: Int,+                         retry_sec :: Int,+                         expire_sec :: Int,+                         ttl_sec :: Int,+                         status :: String,+                         master_ips :: [String],+                         axfr_ips :: [String],+                         display_group :: String } deriving (Eq, Show, GHC.Generics.Generic)++instance Data.Aeson.FromJSON DNSZone+instance Data.Aeson.ToJSON DNSZone++data DNSZones = DNSZones { total_pages :: Int,+                           total_results :: Int,+                           page :: Int,+                           dnszones :: [DNSZone] } deriving (Eq, Show, GHC.Generics.Generic)++instance Data.Aeson.FromJSON DNSZones+instance Data.Aeson.ToJSON DNSZones++data Kernel = Kernel { id :: String,+                       created :: String,+                       deprecated :: Bool,+                       xen :: Bool,+                       kvm :: Bool,+                       label :: String,+                       version :: String,+                       x64 :: Bool } deriving (Eq, Show, GHC.Generics.Generic)++instance Data.Aeson.FromJSON Kernel+instance Data.Aeson.ToJSON Kernel++data Kernels = Kernels { total_pages :: Int,+                         total_results :: Int,+                         page :: Int,+                         kernels :: [Kernel] } deriving (Eq, Show, GHC.Generics.Generic)++instance Data.Aeson.FromJSON Kernels+instance Data.Aeson.ToJSON Kernels+++data LinodeAlert = LinodeAlert { enabled :: Bool,+                                 threshold :: Int } deriving (Eq, Show, GHC.Generics.Generic)++instance Data.Aeson.FromJSON LinodeAlert+instance Data.Aeson.ToJSON LinodeAlert++data LinodeAlerts = LinodeAlerts { cpu :: LinodeAlert,+                                   io :: LinodeAlert,+                                   transfer_in :: LinodeAlert,+                                   transfer_out :: LinodeAlert,+                                   transfer_quota :: LinodeAlert } deriving (Eq, Show, GHC.Generics.Generic)++instance Data.Aeson.FromJSON LinodeAlerts+instance Data.Aeson.ToJSON LinodeAlerts++data LinodeBackupsSchedule = LinodeBackupsSchedule { day :: M.Maybe String,+                                                     window :: M.Maybe String } deriving (Eq, Show, GHC.Generics.Generic)++instance Data.Aeson.FromJSON LinodeBackupsSchedule+instance Data.Aeson.ToJSON LinodeBackupsSchedule++data LinodeBackups = LinodeBackups { enabled :: Bool,+                                     schedule :: LinodeBackupsSchedule,+                                     last_backup :: M.Maybe String } deriving (Eq, Show, GHC.Generics.Generic)++instance Data.Aeson.FromJSON LinodeBackups+instance Data.Aeson.ToJSON LinodeBackups++data LinodeIPAddressesPrivate = LinodeIPAddressesPrivate { ipv4 :: [String],+                                                           link_local :: String } deriving (Eq, Show, GHC.Generics.Generic)++instance Data.Aeson.FromJSON LinodeIPAddressesPrivate+instance Data.Aeson.ToJSON LinodeIPAddressesPrivate++data LinodeIPAddressesPublic = LinodeIPAddressesPublic { failover :: [String],+                                                         ipv4 :: [String],+                                                         ipv6 :: String } deriving (Eq, Show, GHC.Generics.Generic)++instance Data.Aeson.FromJSON LinodeIPAddressesPublic+instance Data.Aeson.ToJSON LinodeIPAddressesPublic++data LinodeIPAddresses = LinodeIPAddresses { private :: LinodeIPAddressesPrivate,+                                             public :: LinodeIPAddressesPublic } deriving (Eq, Show, GHC.Generics.Generic)++instance Data.Aeson.FromJSON LinodeIPAddresses+instance Data.Aeson.ToJSON LinodeIPAddresses++data Linode = Linode { id :: String,+                       alerts :: LinodeAlerts,+                       backups :: LinodeBackups,+                       created :: String,+                       datacenter :: Datacenter,+                       distribution :: M.Maybe Distribution,+                       group :: String,+                       ip_addresses :: LinodeIPAddresses,+                       label :: String,+                       services :: [Service],+                       state :: String,+                       total_transfer :: Int,+                       updated :: String } deriving (Eq, Show, GHC.Generics.Generic)++instance Data.Aeson.FromJSON Linode+instance Data.Aeson.ToJSON Linode++data Linodes = Linodes { total_pages :: Int,+                         total_results :: Int,+                         page :: Int,+                         linodes :: [Linode] } deriving (Eq, Show, GHC.Generics.Generic)++instance Data.Aeson.FromJSON Linodes+instance Data.Aeson.ToJSON Linodes++-- Missing type field+data Backup = Backup { id :: String,+                       label :: String,+                       status :: String,+                       linode_id :: String,+                       datacenter :: Datacenter,+                       created :: String,+                       updated :: String,+                       finished :: String } deriving (Eq, Show, GHC.Generics.Generic)++instance Data.Aeson.FromJSON Backup+instance Data.Aeson.ToJSON Backup++data Backups = Backups { total_pages :: Int,+                         total_results :: Int,+                         page :: Int,+                         backups :: [Backup] } deriving (Eq, Show, GHC.Generics.Generic)++instance Data.Aeson.FromJSON Backups+instance Data.Aeson.ToJSON Backups++data Filesystem = Raw | Swap | Ext3 | Ext4 deriving (Eq, Show)++instance Data.Aeson.ToJSON Filesystem where+  toJSON o =+    let json :: String = case o of+          Raw -> "raw"+          Swap -> "swap"+          Ext3 -> "ext3"+          Ext4 -> "ext4" in+    Data.Aeson.object [ "filesystem" .= json ]++instance Data.Aeson.FromJSON Filesystem where+  parseJSON o =+    return $ case o of+      "raw" -> Raw+      "swap" -> Swap+      "ext3" -> Ext3+      "ext4" -> Ext4++data Disk = Disk { id :: String,+                   label :: String,+                   state :: String,+                   size :: Int,+                   filesystem :: Filesystem,+                   created :: String,+                   updated :: String } deriving (Eq, Show, GHC.Generics.Generic)++instance Data.Aeson.FromJSON Disk+instance Data.Aeson.ToJSON Disk++data Disks = Disks { total_pages :: Int,+                     total_results :: Int,+                     page :: Int,+                     backups :: [Disk] } deriving (Eq, Show, GHC.Generics.Generic)++instance Data.Aeson.FromJSON Disks+instance Data.Aeson.ToJSON Disks++data ConfigDisks = ConfigDisks { sda :: M.Maybe Disk,+                                 sdb :: M.Maybe Disk,+                                 sdc :: M.Maybe Disk,+                                 sdd :: M.Maybe Disk,+                                 sde :: M.Maybe Disk,+                                 sdf :: M.Maybe Disk,+                                 sdg :: M.Maybe Disk,+                                 sdh :: M.Maybe Disk } deriving (Eq, Show, GHC.Generics.Generic)++instance Data.Aeson.FromJSON ConfigDisks+instance Data.Aeson.ToJSON ConfigDisks++data ConfigHelpers = ConfigHelpers { disable_updatedb :: Bool,+                                     enable_distro_helper :: Bool,+                                     enable_modules_deb_helper :: Bool,+                                     enable_network_helper :: Bool } deriving (Eq, Show, GHC.Generics.Generic)++instance Data.Aeson.FromJSON ConfigHelpers+instance Data.Aeson.ToJSON ConfigHelpers++data Config = Config { id :: String,+                       comments :: String,+                       created :: String,+                       devtmpfs_automount :: Bool,+                       disks :: ConfigDisks,+                       helpers :: ConfigHelpers,+                       kernel :: Kernel,+                       label :: String,+                       ram_limit :: M.Maybe Int,+                       root_device :: String,+                       root_device_ro :: Bool,+                       run_level :: String,+                       updated :: String,+                       virt_mode :: String } deriving (Eq, Show, GHC.Generics.Generic)++instance Data.Aeson.FromJSON Config+instance Data.Aeson.ToJSON Config++data Configs = Configs { total_pages :: Int,+                         total_results :: Int,+                         page :: Int,+                         configs :: [Config] } deriving (Eq, Show, GHC.Generics.Generic)+++instance Data.Aeson.FromJSON Configs+instance Data.Aeson.ToJSON Configs++-- Missing default field+data UDF = UDF { name :: String,+                 label :: String,+                 example :: String } deriving (Eq, Show, GHC.Generics.Generic)++instance Data.Aeson.FromJSON UDF+instance Data.Aeson.ToJSON UDF++data StackScript = StackScript { id :: String,+                                 customer_id :: String,+                                 user_id :: String,+                                 label :: String,+                                 description :: String,+                                 distributions :: [Distribution],+                                 deployments_total :: Int,+                                 deployments_active :: Int,+                                 is_public :: Bool,+                                 created :: String,+                                 updated :: String,+                                 rev_note :: String,+                                 script :: String,+                                 user_defined_fields :: [UDF] } deriving (Eq, Show, GHC.Generics.Generic)++instance Data.Aeson.FromJSON StackScript+instance Data.Aeson.ToJSON StackScript++data StackScripts = StackScripts { total_pages :: Int,+                                   total_results :: Int,+                                   page :: Int,+                                   stackscripts :: [StackScripts] } deriving (Eq, Show, GHC.Generics.Generic)++instance Data.Aeson.FromJSON StackScripts+instance Data.Aeson.ToJSON StackScripts++data RunLevel = Default | Single | BinBash deriving (Eq, Show)++instance Data.Aeson.ToJSON RunLevel where+  toJSON o =+    let json :: String = case o of+          Default -> "default"+          Single -> "single"+          BinBash -> "binbash" in+    Data.Aeson.object [ "run_level" .= json ]++instance Data.Aeson.FromJSON RunLevel where+  parseJSON o =+    return $ case o of+      "default" -> Default+      "single" -> Single+      "binbash" -> BinBash++data VirtMode = FullVirt | ParaVirt deriving (Eq, Show)++instance Data.Aeson.ToJSON VirtMode where+  toJSON o =+    let json :: String = case o of+          FullVirt -> "fullvirt"+          ParaVirt -> "paravirt" in+    Data.Aeson.object [ "virt_mode" .= json ]++instance Data.Aeson.FromJSON VirtMode where+  parseJSON o =+    return $ case o of+      "fullvirt" -> FullVirt+      "paravirt" -> ParaVirt