Enhance the Luxi interface implementation
[ganeti-local] / htools / Ganeti / Objects.hs
index a366546..380524c 100644 (file)
@@ -36,11 +36,15 @@ module Ganeti.Objects
   , PartialNIC(..)
   , DiskMode(..)
   , DiskType(..)
+  , DiskLogicalId(..)
   , Disk(..)
   , DiskTemplate(..)
   , PartialBEParams(..)
   , FilledBEParams(..)
   , fillBEParams
+  , Hypervisor(..)
+  , AdminState(..)
+  , adminStateFromRaw
   , Instance(..)
   , toDictInstance
   , PartialNDParams(..)
@@ -48,13 +52,22 @@ module Ganeti.Objects
   , fillNDParams
   , Node(..)
   , AllocPolicy(..)
+  , FilledISpecParams(..)
+  , PartialISpecParams(..)
+  , fillISpecParams
+  , FilledIPolicy(..)
+  , PartialIPolicy(..)
+  , fillIPolicy
   , NodeGroup(..)
+  , IpFamily(..)
+  , ipFamilyToVersion
   , Cluster(..)
   , ConfigData(..)
   ) where
 
 import Data.Maybe
-import Text.JSON (makeObj, showJSON, readJSON)
+import Text.JSON (makeObj, showJSON, readJSON, JSON, JSValue(..))
+import qualified Text.JSON as J
 
 import qualified Ganeti.Constants as C
 import Ganeti.HTools.JSON
@@ -93,16 +106,119 @@ $(declareSADT "DiskType"
   , ("LD_DRBD8",    'C.ldDrbd8)
   , ("LD_FILE",     'C.ldFile)
   , ("LD_BLOCKDEV", 'C.ldBlockdev)
+  , ("LD_RADOS",    'C.ldRbd)
   ])
 $(makeJSONInstance ''DiskType)
 
+-- | The file driver type.
+$(declareSADT "FileDriver"
+  [ ("FileLoop",   'C.fdLoop)
+  , ("FileBlktap", 'C.fdBlktap)
+  ])
+$(makeJSONInstance ''FileDriver)
+
+-- | The persistent block driver type. Currently only one type is allowed.
+$(declareSADT "BlockDriver"
+  [ ("BlockDrvManual", 'C.blockdevDriverManual)
+  ])
+$(makeJSONInstance ''BlockDriver)
+
+-- | Constant for the dev_type key entry in the disk config.
+devType :: String
+devType = "dev_type"
+
+-- | The disk configuration type. This includes the disk type itself,
+-- for a more complete consistency. Note that since in the Python
+-- code-base there's no authoritative place where we document the
+-- logical id, this is probably a good reference point.
+data DiskLogicalId
+  = LIDPlain String String  -- ^ Volume group, logical volume
+  | LIDDrbd8 String String Int Int Int String
+  -- ^ NodeA, NodeB, Port, MinorA, MinorB, Secret
+  | LIDFile FileDriver String -- ^ Driver, path
+  | LIDBlockDev BlockDriver String -- ^ Driver, path (must be under /dev)
+  | LIDRados String String -- ^ Unused, path
+    deriving (Read, Show, Eq)
+
+-- | Mapping from a logical id to a disk type.
+lidDiskType :: DiskLogicalId -> DiskType
+lidDiskType (LIDPlain {}) = LD_LV
+lidDiskType (LIDDrbd8 {}) = LD_DRBD8
+lidDiskType (LIDFile  {}) = LD_FILE
+lidDiskType (LIDBlockDev {}) = LD_BLOCKDEV
+lidDiskType (LIDRados {}) = LD_RADOS
+
+-- | Builds the extra disk_type field for a given logical id.
+lidEncodeType :: DiskLogicalId -> [(String, JSValue)]
+lidEncodeType v = [(devType, showJSON . lidDiskType $ v)]
+
+-- | Custom encoder for DiskLogicalId (logical id only).
+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 (LIDBlockDev driver name) = JSArray [showJSON driver, showJSON name]
+
+-- | Custom encoder for DiskLogicalId, composing both the logical id
+-- and the extra disk_type field.
+encodeFullDLId :: DiskLogicalId -> (JSValue, [(String, JSValue)])
+encodeFullDLId v = (encodeDLId v, lidEncodeType v)
+
+-- | Custom decoder for DiskLogicalId. This is manual for now, since
+-- we don't have yet automation for separate-key style fields.
+decodeDLId :: [(String, JSValue)] -> JSValue -> J.Result DiskLogicalId
+decodeDLId obj lid = do
+  dtype <- fromObj obj devType
+  case dtype of
+    LD_DRBD8 ->
+      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"
+    LD_LV ->
+      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"
+    LD_FILE ->
+      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"
+    LD_BLOCKDEV ->
+      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"
+    LD_RADOS ->
+      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"
+
 -- | Disk data structure.
 --
 -- This is declared manually as it's a recursive structure, and our TH
 -- code currently can't build it.
 data Disk = Disk
-  { diskDevType    :: DiskType
---  , diskLogicalId  :: String
+  { diskLogicalId  :: DiskLogicalId
 --  , diskPhysicalId :: String
   , diskChildren   :: [Disk]
   , diskIvName     :: String
@@ -111,8 +227,8 @@ data Disk = Disk
   } deriving (Read, Show, Eq)
 
 $(buildObjectSerialisation "Disk"
-  [ simpleField "dev_type"      [t| DiskMode |]
---  , simpleField "logical_id"  [t| String   |]
+  [ customField 'decodeDLId 'encodeFullDLId $
+      simpleField "logical_id"    [t| DiskLogicalId   |]
 --  , simpleField "physical_id" [t| String   |]
   , defaultField  [| [] |] $ simpleField "children" [t| [Disk] |]
   , defaultField [| "" |] $ simpleField "iv_name" [t| String |]
@@ -120,6 +236,19 @@ $(buildObjectSerialisation "Disk"
   , defaultField [| DiskRdWr |] $ simpleField "mode" [t| DiskMode |]
   ])
 
+-- * Hypervisor definitions
+
+-- | This may be due to change when we add hypervisor parameters.
+$(declareSADT "Hypervisor"
+  [ ( "Kvm",    'C.htKvm )
+  , ( "XenPvm", 'C.htXenPvm )
+  , ( "Chroot", 'C.htChroot )
+  , ( "XenHvm", 'C.htXenHvm )
+  , ( "Lxc",    'C.htLxc )
+  , ( "Fake",   'C.htFake )
+  ])
+$(makeJSONInstance ''Hypervisor)
+
 -- * Instance definitions
 
 -- | Instance disk template type. **Copied from HTools/Types.hs**
@@ -130,6 +259,7 @@ $(declareSADT "DiskTemplate"
   , ("DTPlain",      'C.dtPlain)
   , ("DTBlock",      'C.dtBlock)
   , ("DTDrbd8",      'C.dtDrbd8)
+  , ("DTRados",      'C.dtRbd)
   ])
 $(makeJSONInstance ''DiskTemplate)
 
@@ -165,10 +295,68 @@ $(buildObject "Instance" "inst" $
   ++ uuidFields
   ++ serialFields)
 
+-- * IPolicy definitions
+
+$(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.ispecSpindleUse  [t| Int |]
+  ])
+
+-- | Custom partial ipolicy. This is not built via buildParam since it
+-- has a special 2-level inheritance mode.
+$(buildObject "PartialIPolicy" "ipolicy" $
+  [ renameField "MinSpecP" $ simpleField "min" [t| PartialISpecParams |]
+  , renameField "MaxSpecP" $ simpleField "max" [t| PartialISpecParams |]
+  , 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] |]
+  ])
+
+-- | Custom filled ipolicy. This is not built via buildParam since it
+-- has a special 2-level inheritance mode.
+$(buildObject "FilledIPolicy" "ipolicy" $
+  [ renameField "MinSpec" $ simpleField "min" [t| FilledISpecParams |]
+  , renameField "MaxSpec" $ simpleField "max" [t| FilledISpecParams |]
+  , renameField "StdSpec" $ simpleField "std" [t| FilledISpecParams |]
+  , simpleField "spindle-ratio"  [t| Double |]
+  , simpleField "vcpu-ratio"     [t| Double |]
+  , simpleField "disk-templates" [t| [DiskTemplate] |]
+  ])
+
+-- | Custom filler for the ipolicy types.
+fillIPolicy :: FilledIPolicy -> PartialIPolicy -> FilledIPolicy
+fillIPolicy (FilledIPolicy { ipolicyMinSpec       = fmin
+                           , ipolicyMaxSpec       = fmax
+                           , ipolicyStdSpec       = fstd
+                           , ipolicySpindleRatio  = fspindleRatio
+                           , ipolicyVcpuRatio     = fvcpuRatio
+                           , ipolicyDiskTemplates = fdiskTemplates})
+            (PartialIPolicy { ipolicyMinSpecP       = pmin
+                            , ipolicyMaxSpecP       = pmax
+                            , ipolicyStdSpecP       = pstd
+                            , ipolicySpindleRatioP  = pspindleRatio
+                            , ipolicyVcpuRatioP     = pvcpuRatio
+                            , ipolicyDiskTemplatesP = pdiskTemplates}) =
+  FilledIPolicy { ipolicyMinSpec       = fillISpecParams fmin pmin
+                , ipolicyMaxSpec       = fillISpecParams fmax pmax
+                , ipolicyStdSpec       = fillISpecParams fstd pstd
+                , ipolicySpindleRatio  = fromMaybe fspindleRatio pspindleRatio
+                , ipolicyVcpuRatio     = fromMaybe fvcpuRatio pvcpuRatio
+                , ipolicyDiskTemplates = fromMaybe fdiskTemplates
+                                         pdiskTemplates
+                }
 -- * Node definitions
 
 $(buildParam "ND" "ndp" $
-  [ simpleField "oob_program" [t| String |]
+  [ simpleField "oob_program"   [t| String |]
+  , simpleField "spindle_count" [t| Int    |]
   ])
 
 $(buildObject "Node" "node" $
@@ -181,7 +369,7 @@ $(buildObject "Node" "node" $
   , simpleField "group"            [t| String |]
   , simpleField "master_capable"   [t| Bool   |]
   , simpleField "vm_capable"       [t| Bool   |]
---  , simpleField "ndparams"       [t| PartialNDParams |]
+  , simpleField "ndparams"         [t| PartialNDParams |]
   , simpleField "powered"          [t| Bool   |]
   ]
   ++ timeStampFields
@@ -208,13 +396,27 @@ $(makeJSONInstance ''AllocPolicy)
 $(buildObject "NodeGroup" "group" $
   [ simpleField "name"         [t| String |]
   , defaultField  [| [] |] $ simpleField "members" [t| [String] |]
---  , simpleField "ndparams"   [t| PartialNDParams |]
-  , simpleField "alloc_policy" [t| AllocPolicy |]
+  , simpleField "ndparams"     [t| PartialNDParams |]
+  , simpleField "alloc_policy" [t| AllocPolicy     |]
+  , simpleField "ipolicy"      [t| PartialIPolicy  |]
   ]
   ++ timeStampFields
   ++ uuidFields
   ++ serialFields)
 
+-- | IP family type
+$(declareIADT "IpFamily"
+  [ ("IpFamilyV4", 'C.ip4Family)
+  , ("IpFamilyV6", 'C.ip6Family)
+  ])
+$(makeJSONInstance ''IpFamily)
+
+-- | Conversion from IP family to IP version. This is needed because
+-- Python uses both, depending on context.
+ipFamilyToVersion :: IpFamily -> Int
+ipFamilyToVersion IpFamilyV4 = C.ip4Version
+ipFamilyToVersion IpFamilyV6 = C.ip6Version
+
 -- * Cluster definitions
 $(buildObject "Cluster" "cluster" $
   [ simpleField "rsahostkeypub"             [t| String   |]
@@ -223,44 +425,49 @@ $(buildObject "Cluster" "cluster" $
   , simpleField "mac_prefix"                [t| String   |]
   , simpleField "volume_group_name"         [t| String   |]
   , simpleField "reserved_lvs"              [t| [String] |]
---  , simpleField "drbd_usermode_helper"      [t| String   |]
+  , optionalField $ simpleField "drbd_usermode_helper" [t| String |]
 -- , simpleField "default_bridge"          [t| String   |]
 -- , simpleField "default_hypervisor"      [t| String   |]
   , simpleField "master_node"               [t| String   |]
   , simpleField "master_ip"                 [t| String   |]
   , simpleField "master_netdev"             [t| String   |]
--- , simpleField "master_netmask"          [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 "shared_file_storage_dir"   [t| String   |]
   , simpleField "enabled_hypervisors"       [t| [String] |]
 -- , simpleField "hvparams"                [t| [(String, [(String, String)])] |]
 -- , simpleField "os_hvp"                  [t| [(String, String)] |]
-  , containerField $ simpleField "beparams" [t| FilledBEParams |]
--- , simpleField "osparams"                [t| [(String, String)] |]
-  , containerField $ simpleField "nicparams" [t| FilledNICParams    |]
---  , simpleField "ndparams"                  [t| FilledNDParams |]
+  , simpleField "beparams" [t| Container FilledBEParams |]
+  , simpleField "osparams"                  [t| Container (Container String) |]
+  , simpleField "nicparams" [t| Container FilledNICParams    |]
+  , simpleField "ndparams"                  [t| FilledNDParams |]
   , 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| [Int]              |]
+  , simpleField "uid_pool"                  [t| [(Int, Int)]       |]
   , simpleField "default_iallocator"        [t| String             |]
   , simpleField "hidden_os"                 [t| [String]           |]
   , simpleField "blacklisted_os"            [t| [String]           |]
-  , simpleField "primary_ip_family"         [t| Int                |]
+  , simpleField "primary_ip_family"         [t| IpFamily           |]
   , simpleField "prealloc_wipe_disks"       [t| Bool               |]
+  , simpleField "ipolicy"                   [t| FilledIPolicy      |]
  ]
- ++ serialFields)
+ ++ serialFields
+ ++ timeStampFields
+ ++ uuidFields
+ ++ tagsFields)
 
 -- * ConfigData definitions
 
 $(buildObject "ConfigData" "config" $
 --  timeStampFields ++
-  [ simpleField "version"       [t| Int                |]
-  , simpleField "cluster"       [t| Cluster            |]
-  , containerField $ simpleField "nodes"      [t| Node     |]
-  , containerField $ simpleField "nodegroups" [t| NodeGroup |]
-  , containerField $ simpleField "instances"  [t| Instance |]
+  [ simpleField "version"    [t| Int                 |]
+  , simpleField "cluster"    [t| Cluster             |]
+  , simpleField "nodes"      [t| Container Node      |]
+  , simpleField "nodegroups" [t| Container NodeGroup |]
+  , simpleField "instances"  [t| Container Instance  |]
   ]
   ++ serialFields)