module Ganeti.Objects
( VType(..)
, vTypeFromRaw
, HvParams
, OsParams
, PartialNicParams(..)
, FilledNicParams(..)
, fillNicParams
, allNicParamFields
, PartialNic(..)
, FileDriver(..)
, BlockDriver(..)
, DiskMode(..)
, DiskLogicalId(..)
, Disk(..)
, includesLogicalId
, DiskTemplate(..)
, PartialBeParams(..)
, FilledBeParams(..)
, fillBeParams
, allBeParamFields
, AdminState(..)
, adminStateFromRaw
, Instance(..)
, toDictInstance
, PartialNDParams(..)
, FilledNDParams(..)
, fillNDParams
, allNDParamFields
, Node(..)
, NodeRole(..)
, nodeRoleToRaw
, roleDescription
, AllocPolicy(..)
, FilledISpecParams(..)
, PartialISpecParams(..)
, fillISpecParams
, allISpecParamFields
, MinMaxISpecs(..)
, FilledIPolicy(..)
, PartialIPolicy(..)
, fillIPolicy
, DiskParams
, NodeGroup(..)
, IpFamily(..)
, ipFamilyToVersion
, fillDict
, ClusterHvParams
, OsHvParams
, ClusterBeParams
, ClusterOsParams
, ClusterNicParams
, Cluster(..)
, ConfigData(..)
, TimeStampObject(..)
, UuidObject(..)
, SerialNoObject(..)
, TagsObject(..)
, DictObject(..)
, TagSet
, Network(..)
, Ip4Address(..)
, Ip4Network(..)
, readIp4Address
, nextIp4Address
) where
import Control.Applicative
import Data.List (foldl')
import Data.Maybe
import qualified Data.Map as Map
import qualified Data.Set as Set
import Data.Word
import Text.JSON (showJSON, readJSON, JSON, JSValue(..), fromJSString)
import qualified Text.JSON as J
import qualified Ganeti.Constants as C
import Ganeti.JSON
import Ganeti.Types
import Ganeti.THH
import Ganeti.Utils (sepSplit, tryRead)
fillDict :: (Ord k) => Map.Map k v -> Map.Map k v -> [k] -> Map.Map k v
fillDict defaults custom skip_keys =
let updated = Map.union custom defaults
in foldl' (flip Map.delete) updated skip_keys
$(declareSADT "VType"
[ ("VTypeString", 'C.vtypeString)
, ("VTypeMaybeString", 'C.vtypeMaybeString)
, ("VTypeBool", 'C.vtypeBool)
, ("VTypeSize", 'C.vtypeSize)
, ("VTypeInt", 'C.vtypeInt)
])
$(makeJSONInstance ''VType)
type HvParams = Container JSValue
type OsParams = Container String
class TimeStampObject a where
cTimeOf :: a -> Double
mTimeOf :: a -> Double
class UuidObject a where
uuidOf :: a -> String
class SerialNoObject a where
serialOf :: a -> Int
class TagsObject a where
tagsOf :: a -> Set.Set String
$(declareSADT "NodeRole"
[ ("NROffline", 'C.nrOffline)
, ("NRDrained", 'C.nrDrained)
, ("NRRegular", 'C.nrRegular)
, ("NRCandidate", 'C.nrMcandidate)
, ("NRMaster", 'C.nrMaster)
])
$(makeJSONInstance ''NodeRole)
roleDescription :: NodeRole -> String
roleDescription NROffline = "offline"
roleDescription NRDrained = "drained"
roleDescription NRRegular = "regular"
roleDescription NRCandidate = "master candidate"
roleDescription NRMaster = "master"
data Ip4Address = Ip4Address Word8 Word8 Word8 Word8
deriving Eq
instance Show Ip4Address where
show (Ip4Address a b c d) = show a ++ "." ++ show b ++ "." ++
show c ++ "." ++ show d
readIp4Address :: (Applicative m, Monad m) => String -> m Ip4Address
readIp4Address s =
case sepSplit '.' s of
[a, b, c, d] -> Ip4Address <$>
tryRead "first octect" a <*>
tryRead "second octet" b <*>
tryRead "third octet" c <*>
tryRead "fourth octet" d
_ -> fail $ "Can't parse IPv4 address from string " ++ s
instance JSON Ip4Address where
showJSON = showJSON . show
readJSON (JSString s) = readIp4Address (fromJSString s)
readJSON v = fail $ "Invalid JSON value " ++ show v ++ " for an IPv4 address"
nextIp4Address :: Ip4Address -> Ip4Address
nextIp4Address (Ip4Address a b c d) =
let inc xs y = if all (==0) xs then y + 1 else y
d' = d + 1
c' = inc [d'] c
b' = inc [c', d'] b
a' = inc [b', c', d'] a
in Ip4Address a' b' c' d'
data Ip4Network = Ip4Network Ip4Address Word8
deriving Eq
instance Show Ip4Network where
show (Ip4Network ip netmask) = show ip ++ "/" ++ show netmask
instance JSON Ip4Network where
showJSON = showJSON . show
readJSON (JSString s) =
case sepSplit '/' (fromJSString s) of
[ip, nm] -> do
ip' <- readIp4Address ip
nm' <- tryRead "parsing netmask" nm
if nm' >= 0 && nm' <= 32
then return $ Ip4Network ip' nm'
else fail $ "Invalid netmask " ++ show nm' ++ " from string " ++
fromJSString s
_ -> fail $ "Can't parse IPv4 network from string " ++ fromJSString s
readJSON v = fail $ "Invalid JSON value " ++ show v ++ " for an IPv4 network"
$(buildObject "Network" "network" $
[ simpleField "name" [t| NonEmptyString |]
, optionalField $
simpleField "mac_prefix" [t| String |]
, simpleField "network" [t| Ip4Network |]
, optionalField $
simpleField "network6" [t| String |]
, optionalField $
simpleField "gateway" [t| Ip4Address |]
, optionalField $
simpleField "gateway6" [t| String |]
, optionalField $
simpleField "reservations" [t| String |]
, optionalField $
simpleField "ext_reservations" [t| String |]
]
++ uuidFields
++ timeStampFields
++ serialFields
++ tagsFields)
instance SerialNoObject Network where
serialOf = networkSerial
instance TagsObject Network where
tagsOf = networkTags
instance UuidObject Network where
uuidOf = networkUuid
instance TimeStampObject Network where
cTimeOf = networkCtime
mTimeOf = networkMtime
$(buildParam "Nic" "nicp"
[ simpleField "mode" [t| NICMode |]
, simpleField "link" [t| String |]
])
$(buildObject "PartialNic" "nic" $
[ simpleField "mac" [t| String |]
, optionalField $ simpleField "ip" [t| String |]
, simpleField "nicparams" [t| PartialNicParams |]
, optionalField $ simpleField "network" [t| String |]
, optionalField $ simpleField "name" [t| String |]
] ++ uuidFields)
instance UuidObject PartialNic where
uuidOf = nicUuid
$(declareSADT "DiskMode"
[ ("DiskRdOnly", 'C.diskRdonly)
, ("DiskRdWr", 'C.diskRdwr)
])
$(makeJSONInstance ''DiskMode)
$(declareSADT "BlockDriver"
[ ("BlockDrvManual", 'C.blockdevDriverManual)
])
$(makeJSONInstance ''BlockDriver)
devType :: String
devType = "dev_type"
data DiskLogicalId
= LIDPlain String String
| LIDDrbd8 String String Int Int Int String
| LIDFile FileDriver String
| LIDSharedFile FileDriver String
| LIDBlockDev BlockDriver String
| LIDRados String String
| LIDExt String String
deriving (Show, Eq)
lidDiskType :: DiskLogicalId -> DiskTemplate
lidDiskType (LIDPlain {}) = DTPlain
lidDiskType (LIDDrbd8 {}) = DTDrbd8
lidDiskType (LIDFile {}) = DTFile
lidDiskType (LIDSharedFile {}) = DTSharedFile
lidDiskType (LIDBlockDev {}) = DTBlock
lidDiskType (LIDRados {}) = DTRbd
lidDiskType (LIDExt {}) = DTExt
lidEncodeType :: DiskLogicalId -> [(String, JSValue)]
lidEncodeType v = [(devType, showJSON . lidDiskType $ v)]
encodeDLId :: DiskLogicalId -> JSValue
encodeDLId (LIDPlain vg lv) = JSArray [showJSON vg, showJSON lv]
encodeDLId (LIDDrbd8 nodeA nodeB port minorA minorB key) =
JSArray [ showJSON nodeA, showJSON nodeB, showJSON port
, showJSON minorA, showJSON minorB, showJSON key ]
encodeDLId (LIDRados pool name) = JSArray [showJSON pool, showJSON name]
encodeDLId (LIDFile driver name) = JSArray [showJSON driver, showJSON name]
encodeDLId (LIDSharedFile driver name) =
JSArray [showJSON driver, showJSON name]
encodeDLId (LIDBlockDev driver name) = JSArray [showJSON driver, showJSON name]
encodeDLId (LIDExt extprovider name) =
JSArray [showJSON extprovider, showJSON name]
encodeFullDLId :: DiskLogicalId -> (JSValue, [(String, JSValue)])
encodeFullDLId v = (encodeDLId v, lidEncodeType v)
decodeDLId :: [(String, JSValue)] -> JSValue -> J.Result DiskLogicalId
decodeDLId obj lid = do
dtype <- fromObj obj devType
case dtype of
DTDrbd8 ->
case lid of
JSArray [nA, nB, p, mA, mB, k] -> do
nA' <- readJSON nA
nB' <- readJSON nB
p' <- readJSON p
mA' <- readJSON mA
mB' <- readJSON mB
k' <- readJSON k
return $ LIDDrbd8 nA' nB' p' mA' mB' k'
_ -> fail "Can't read logical_id for DRBD8 type"
DTPlain ->
case lid of
JSArray [vg, lv] -> do
vg' <- readJSON vg
lv' <- readJSON lv
return $ LIDPlain vg' lv'
_ -> fail "Can't read logical_id for plain type"
DTFile ->
case lid of
JSArray [driver, path] -> do
driver' <- readJSON driver
path' <- readJSON path
return $ LIDFile driver' path'
_ -> fail "Can't read logical_id for file type"
DTSharedFile ->
case lid of
JSArray [driver, path] -> do
driver' <- readJSON driver
path' <- readJSON path
return $ LIDSharedFile driver' path'
_ -> fail "Can't read logical_id for shared file type"
DTBlock ->
case lid of
JSArray [driver, path] -> do
driver' <- readJSON driver
path' <- readJSON path
return $ LIDBlockDev driver' path'
_ -> fail "Can't read logical_id for blockdev type"
DTRbd ->
case lid of
JSArray [driver, path] -> do
driver' <- readJSON driver
path' <- readJSON path
return $ LIDRados driver' path'
_ -> fail "Can't read logical_id for rdb type"
DTExt ->
case lid of
JSArray [extprovider, name] -> do
extprovider' <- readJSON extprovider
name' <- readJSON name
return $ LIDExt extprovider' name'
_ -> fail "Can't read logical_id for extstorage type"
DTDiskless ->
fail "Retrieved 'diskless' disk."
data Disk = Disk
{ diskLogicalId :: DiskLogicalId
, diskChildren :: [Disk]
, diskIvName :: String
, diskSize :: Int
, diskMode :: DiskMode
, diskName :: Maybe String
, diskSpindles :: Maybe Int
, diskUuid :: String
} deriving (Show, Eq)
$(buildObjectSerialisation "Disk" $
[ customField 'decodeDLId 'encodeFullDLId ["dev_type"] $
simpleField "logical_id" [t| DiskLogicalId |]
, defaultField [| [] |] $ simpleField "children" [t| [Disk] |]
, defaultField [| "" |] $ simpleField "iv_name" [t| String |]
, simpleField "size" [t| Int |]
, defaultField [| DiskRdWr |] $ simpleField "mode" [t| DiskMode |]
, optionalField $ simpleField "name" [t| String |]
, optionalField $ simpleField "spindles" [t| Int |]
]
++ uuidFields)
instance UuidObject Disk where
uuidOf = diskUuid
includesLogicalId :: String -> String -> Disk -> Bool
includesLogicalId vg_name lv_name disk =
case diskLogicalId disk of
LIDPlain vg lv -> vg_name == vg && lv_name == lv
LIDDrbd8 {} ->
any (includesLogicalId vg_name lv_name) $ diskChildren disk
_ -> False
$(declareSADT "AdminState"
[ ("AdminOffline", 'C.adminstOffline)
, ("AdminDown", 'C.adminstDown)
, ("AdminUp", 'C.adminstUp)
])
$(makeJSONInstance ''AdminState)
$(buildParam "Be" "bep"
[ simpleField "minmem" [t| Int |]
, simpleField "maxmem" [t| Int |]
, simpleField "vcpus" [t| Int |]
, simpleField "auto_balance" [t| Bool |]
])
$(buildObject "Instance" "inst" $
[ simpleField "name" [t| String |]
, simpleField "primary_node" [t| String |]
, simpleField "os" [t| String |]
, simpleField "hypervisor" [t| Hypervisor |]
, simpleField "hvparams" [t| HvParams |]
, simpleField "beparams" [t| PartialBeParams |]
, simpleField "osparams" [t| OsParams |]
, simpleField "admin_state" [t| AdminState |]
, simpleField "nics" [t| [PartialNic] |]
, simpleField "disks" [t| [Disk] |]
, simpleField "disk_template" [t| DiskTemplate |]
, simpleField "disks_active" [t| Bool |]
, optionalField $ simpleField "network_port" [t| Int |]
]
++ timeStampFields
++ uuidFields
++ serialFields
++ tagsFields)
instance TimeStampObject Instance where
cTimeOf = instCtime
mTimeOf = instMtime
instance UuidObject Instance where
uuidOf = instUuid
instance SerialNoObject Instance where
serialOf = instSerial
instance TagsObject Instance where
tagsOf = instTags
$(buildParam "ISpec" "ispec"
[ simpleField C.ispecMemSize [t| Int |]
, simpleField C.ispecDiskSize [t| Int |]
, simpleField C.ispecDiskCount [t| Int |]
, simpleField C.ispecCpuCount [t| Int |]
, simpleField C.ispecNicCount [t| Int |]
, simpleField C.ispecSpindleUse [t| Int |]
])
$(buildObject "MinMaxISpecs" "mmis"
[ renameField "MinSpec" $ simpleField "min" [t| FilledISpecParams |]
, renameField "MaxSpec" $ simpleField "max" [t| FilledISpecParams |]
])
$(buildObject "PartialIPolicy" "ipolicy"
[ optionalField . renameField "MinMaxISpecsP"
$ simpleField C.ispecsMinmax [t| [MinMaxISpecs] |]
, optionalField . renameField "StdSpecP"
$ simpleField "std" [t| PartialISpecParams |]
, optionalField . renameField "SpindleRatioP"
$ simpleField "spindle-ratio" [t| Double |]
, optionalField . renameField "VcpuRatioP"
$ simpleField "vcpu-ratio" [t| Double |]
, optionalField . renameField "DiskTemplatesP"
$ simpleField "disk-templates" [t| [DiskTemplate] |]
])
$(buildObject "FilledIPolicy" "ipolicy"
[ renameField "MinMaxISpecs"
$ simpleField C.ispecsMinmax [t| [MinMaxISpecs] |]
, renameField "StdSpec" $ simpleField "std" [t| FilledISpecParams |]
, simpleField "spindle-ratio" [t| Double |]
, simpleField "vcpu-ratio" [t| Double |]
, simpleField "disk-templates" [t| [DiskTemplate] |]
])
fillIPolicy :: FilledIPolicy -> PartialIPolicy -> FilledIPolicy
fillIPolicy (FilledIPolicy { ipolicyMinMaxISpecs = fminmax
, ipolicyStdSpec = fstd
, ipolicySpindleRatio = fspindleRatio
, ipolicyVcpuRatio = fvcpuRatio
, ipolicyDiskTemplates = fdiskTemplates})
(PartialIPolicy { ipolicyMinMaxISpecsP = pminmax
, ipolicyStdSpecP = pstd
, ipolicySpindleRatioP = pspindleRatio
, ipolicyVcpuRatioP = pvcpuRatio
, ipolicyDiskTemplatesP = pdiskTemplates}) =
FilledIPolicy { ipolicyMinMaxISpecs = fromMaybe fminmax pminmax
, ipolicyStdSpec = case pstd of
Nothing -> fstd
Just p -> fillISpecParams fstd p
, ipolicySpindleRatio = fromMaybe fspindleRatio pspindleRatio
, ipolicyVcpuRatio = fromMaybe fvcpuRatio pvcpuRatio
, ipolicyDiskTemplates = fromMaybe fdiskTemplates
pdiskTemplates
}
$(buildParam "ND" "ndp"
[ simpleField "oob_program" [t| String |]
, simpleField "spindle_count" [t| Int |]
, simpleField "exclusive_storage" [t| Bool |]
])
$(buildObject "Node" "node" $
[ simpleField "name" [t| String |]
, simpleField "primary_ip" [t| String |]
, simpleField "secondary_ip" [t| String |]
, simpleField "master_candidate" [t| Bool |]
, simpleField "offline" [t| Bool |]
, simpleField "drained" [t| Bool |]
, simpleField "group" [t| String |]
, simpleField "master_capable" [t| Bool |]
, simpleField "vm_capable" [t| Bool |]
, simpleField "ndparams" [t| PartialNDParams |]
, simpleField "powered" [t| Bool |]
]
++ timeStampFields
++ uuidFields
++ serialFields
++ tagsFields)
instance TimeStampObject Node where
cTimeOf = nodeCtime
mTimeOf = nodeMtime
instance UuidObject Node where
uuidOf = nodeUuid
instance SerialNoObject Node where
serialOf = nodeSerial
instance TagsObject Node where
tagsOf = nodeTags
type DiskParams = Container (Container JSValue)
type Networks = Container PartialNicParams
$(buildObject "NodeGroup" "group" $
[ simpleField "name" [t| String |]
, defaultField [| [] |] $ simpleField "members" [t| [String] |]
, simpleField "ndparams" [t| PartialNDParams |]
, simpleField "alloc_policy" [t| AllocPolicy |]
, simpleField "ipolicy" [t| PartialIPolicy |]
, simpleField "diskparams" [t| DiskParams |]
, simpleField "networks" [t| Networks |]
]
++ timeStampFields
++ uuidFields
++ serialFields
++ tagsFields)
instance TimeStampObject NodeGroup where
cTimeOf = groupCtime
mTimeOf = groupMtime
instance UuidObject NodeGroup where
uuidOf = groupUuid
instance SerialNoObject NodeGroup where
serialOf = groupSerial
instance TagsObject NodeGroup where
tagsOf = groupTags
$(declareIADT "IpFamily"
[ ("IpFamilyV4", 'C.ip4Family)
, ("IpFamilyV6", 'C.ip6Family)
])
$(makeJSONInstance ''IpFamily)
ipFamilyToVersion :: IpFamily -> Int
ipFamilyToVersion IpFamilyV4 = C.ip4Version
ipFamilyToVersion IpFamilyV6 = C.ip6Version
type ClusterHvParams = Container HvParams
type OsHvParams = Container ClusterHvParams
type ClusterBeParams = Container FilledBeParams
type ClusterOsParams = Container OsParams
type ClusterNicParams = Container FilledNicParams
type UidPool = [(Int, Int)]
$(buildObject "Cluster" "cluster" $
[ simpleField "rsahostkeypub" [t| String |]
, optionalField $
simpleField "dsahostkeypub" [t| String |]
, simpleField "highest_used_port" [t| Int |]
, simpleField "tcpudp_port_pool" [t| [Int] |]
, simpleField "mac_prefix" [t| String |]
, optionalField $
simpleField "volume_group_name" [t| String |]
, simpleField "reserved_lvs" [t| [String] |]
, optionalField $
simpleField "drbd_usermode_helper" [t| String |]
, simpleField "master_node" [t| String |]
, simpleField "master_ip" [t| String |]
, simpleField "master_netdev" [t| String |]
, simpleField "master_netmask" [t| Int |]
, simpleField "use_external_mip_script" [t| Bool |]
, simpleField "cluster_name" [t| String |]
, simpleField "file_storage_dir" [t| String |]
, simpleField "shared_file_storage_dir" [t| String |]
, simpleField "enabled_hypervisors" [t| [Hypervisor] |]
, simpleField "hvparams" [t| ClusterHvParams |]
, simpleField "os_hvp" [t| OsHvParams |]
, simpleField "beparams" [t| ClusterBeParams |]
, simpleField "osparams" [t| ClusterOsParams |]
, simpleField "nicparams" [t| ClusterNicParams |]
, simpleField "ndparams" [t| FilledNDParams |]
, simpleField "diskparams" [t| DiskParams |]
, simpleField "candidate_pool_size" [t| Int |]
, simpleField "modify_etc_hosts" [t| Bool |]
, simpleField "modify_ssh_setup" [t| Bool |]
, simpleField "maintain_node_health" [t| Bool |]
, simpleField "uid_pool" [t| UidPool |]
, simpleField "default_iallocator" [t| String |]
, simpleField "hidden_os" [t| [String] |]
, simpleField "blacklisted_os" [t| [String] |]
, simpleField "primary_ip_family" [t| IpFamily |]
, simpleField "prealloc_wipe_disks" [t| Bool |]
, simpleField "ipolicy" [t| FilledIPolicy |]
, simpleField "enabled_disk_templates" [t| [DiskTemplate] |]
]
++ timeStampFields
++ uuidFields
++ serialFields
++ tagsFields)
instance TimeStampObject Cluster where
cTimeOf = clusterCtime
mTimeOf = clusterMtime
instance UuidObject Cluster where
uuidOf = clusterUuid
instance SerialNoObject Cluster where
serialOf = clusterSerial
instance TagsObject Cluster where
tagsOf = clusterTags
$(buildObject "ConfigData" "config" $
[ simpleField "version" [t| Int |]
, simpleField "cluster" [t| Cluster |]
, simpleField "nodes" [t| Container Node |]
, simpleField "nodegroups" [t| Container NodeGroup |]
, simpleField "instances" [t| Container Instance |]
, simpleField "networks" [t| Container Network |]
]
++ serialFields)
instance SerialNoObject ConfigData where
serialOf = configSerial