{-# LANGUAGE TemplateHaskell #-}

{-| Implementation of opcodes parameters.

These are defined in a separate module only due to TemplateHaskell
stage restrictions - expressions defined in the current module can't
be passed to splices. So we have to either parameters/repeat each
parameter definition multiple times, or separate them into this
module.

-}

{-

Copyright (C) 2012, 2014 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.OpParams
  ( ReplaceDisksMode(..)
  , DiskIndex
  , mkDiskIndex
  , unDiskIndex
  , DiskAccess(..)
  , INicParams(..)
  , IDiskParams(..)
  , RecreateDisksInfo(..)
  , DdmOldChanges(..)
  , SetParamsMods(..)
  , ExportTarget(..)
  , pInstanceName
  , pInstanceCommunication
  , pOptInstanceCommunication
  , pInstanceUuid
  , pInstances
  , pName
  , pTagsList
  , pTagsObject
  , pTagsName
  , pOutputFields
  , pShutdownTimeout
  , pShutdownTimeout'
  , pShutdownInstance
  , pForce
  , pIgnoreOfflineNodes
  , pNodeName
  , pNodeUuid
  , pNodeNames
  , pNodeUuids
  , pGroupName
  , pMigrationMode
  , pMigrationLive
  , pMigrationCleanup
  , pForceVariant
  , pWaitForSync
  , pWaitForSyncFalse
  , pIgnoreConsistency
  , pStorageName
  , pUseLocking
  , pOpportunisticLocking
  , pNameCheck
  , pNodeGroupAllocPolicy
  , pGroupNodeParams
  , pQueryWhat
  , pEarlyRelease
  , pIpCheck
  , pIpConflictsCheck
  , pNoRemember
  , pMigrationTargetNode
  , pMigrationTargetNodeUuid
  , pMoveTargetNode
  , pMoveTargetNodeUuid
  , pMoveCompress
  , pBackupCompress
  , pStartupPaused
  , pVerbose
  , pDebugSimulateErrors
  , pErrorCodes
  , pSkipChecks
  , pIgnoreErrors
  , pOptGroupName
  , pDiskParams
  , pHvState
  , pDiskState
  , pIgnoreIpolicy
  , pHotplug
  , pHotplugIfPossible
  , pAllowRuntimeChgs
  , pInstDisks
  , pDiskTemplate
  , pOptDiskTemplate
  , pFileDriver
  , pFileStorageDir
  , pClusterFileStorageDir
  , pClusterSharedFileStorageDir
  , pClusterGlusterStorageDir
  , pInstanceCommunicationNetwork
  , pZeroingImage
  , pVgName
  , pEnabledHypervisors
  , pHypervisor
  , pClusterHvParams
  , pInstHvParams
  , pClusterBeParams
  , pInstBeParams
  , pResetDefaults
  , pOsHvp
  , pClusterOsParams
  , pClusterOsParamsPrivate
  , pInstOsParams
  , pInstOsParamsPrivate
  , pInstOsParamsSecret
  , pCandidatePoolSize
  , pMaxRunningJobs
  , pUidPool
  , pAddUids
  , pRemoveUids
  , pMaintainNodeHealth
  , pModifyEtcHosts
  , pPreallocWipeDisks
  , pNicParams
  , pInstNics
  , pNdParams
  , pIpolicy
  , pDrbdHelper
  , pDefaultIAllocator
  , pDefaultIAllocatorParams
  , pMasterNetdev
  , pMasterNetmask
  , pReservedLvs
  , pHiddenOs
  , pBlacklistedOs
  , pUseExternalMipScript
  , pQueryFields
  , pQueryFilter
  , pQueryFieldsFields
  , pOobCommand
  , pOobTimeout
  , pIgnoreStatus
  , pPowerDelay
  , pPrimaryIp
  , pSecondaryIp
  , pReadd
  , pNodeGroup
  , pMasterCapable
  , pVmCapable
  , pNames
  , pNodes
  , pRequiredNodes
  , pRequiredNodeUuids
  , pStorageType
  , pOptStorageType
  , pStorageChanges
  , pMasterCandidate
  , pOffline
  , pDrained
  , pAutoPromote
  , pPowered
  , pIallocator
  , pRemoteNode
  , pRemoteNodeUuid
  , pEvacMode
  , pInstCreateMode
  , pNoInstall
  , pInstOs
  , pPrimaryNode
  , pPrimaryNodeUuid
  , pSecondaryNode
  , pSecondaryNodeUuid
  , pSourceHandshake
  , pSourceInstance
  , pSourceShutdownTimeout
  , pSourceX509Ca
  , pSrcNode
  , pSrcNodeUuid
  , pSrcPath
  , pStartInstance
  , pInstTags
  , pMultiAllocInstances
  , pTempOsParams
  , pTempOsParamsPrivate
  , pTempOsParamsSecret
  , pTempHvParams
  , pTempBeParams
  , pIgnoreFailures
  , pNewName
  , pIgnoreSecondaries
  , pRebootType
  , pIgnoreDiskSize
  , pRecreateDisksInfo
  , pStatic
  , pInstParamsNicChanges
  , pInstParamsDiskChanges
  , pRuntimeMem
  , pOsNameChange
  , pDiskIndex
  , pDiskChgAmount
  , pDiskChgAbsolute
  , pTargetGroups
  , pExportMode
  , pExportTargetNode
  , pExportTargetNodeUuid
  , pRemoveInstance
  , pIgnoreRemoveFailures
  , pX509KeyName
  , pX509DestCA
  , pZeroFreeSpace
  , pZeroingTimeoutFixed
  , pZeroingTimeoutPerMiB
  , pTagSearchPattern
  , pRestrictedCommand
  , pReplaceDisksMode
  , pReplaceDisksList
  , pAllowFailover
  , pDelayDuration
  , pDelayOnMaster
  , pDelayOnNodes
  , pDelayOnNodeUuids
  , pDelayRepeat
  , pDelayInterruptible
  , pIAllocatorDirection
  , pIAllocatorMode
  , pIAllocatorReqName
  , pIAllocatorNics
  , pIAllocatorDisks
  , pIAllocatorMemory
  , pIAllocatorVCpus
  , pIAllocatorOs
  , pIAllocatorInstances
  , pIAllocatorEvacMode
  , pIAllocatorSpindleUse
  , pIAllocatorCount
  , pJQueueNotifyWaitLock
  , pJQueueNotifyExec
  , pJQueueLogMessages
  , pJQueueFail
  , pTestDummyResult
  , pTestDummyMessages
  , pTestDummyFail
  , pTestDummySubmitJobs
  , pNetworkName
  , pNetworkAddress4
  , pNetworkGateway4
  , pNetworkAddress6
  , pNetworkGateway6
  , pNetworkMacPrefix
  , pNetworkAddRsvdIps
  , pNetworkRemoveRsvdIps
  , pNetworkMode
  , pNetworkLink
  , pDryRun
  , pDebugLevel
  , pOpPriority
  , pDependencies
  , pComment
  , pReason
  , pEnabledDiskTemplates
  ) where

import Control.Monad (liftM, mplus)
import Text.JSON (JSON, JSValue(..), JSObject (..), readJSON, showJSON,
                  fromJSString, toJSObject)
import qualified Text.JSON
import Text.JSON.Pretty (pp_value)

import Ganeti.BasicTypes
import qualified Ganeti.Constants as C
import Ganeti.THH
import Ganeti.THH.Field
import Ganeti.Utils
import Ganeti.JSON
import Ganeti.Types
import qualified Ganeti.Query.Language as Qlang

-- * Helper functions and types

-- | Build a boolean field.
booleanField :: String -> Field
booleanField = flip simpleField [t| Bool |]

-- | Default a field to 'False'.
defaultFalse :: String -> Field
defaultFalse = defaultField [| False |] . booleanField

-- | Default a field to 'True'.
defaultTrue :: String -> Field
defaultTrue = defaultField [| True |] . booleanField

-- | An alias for a 'String' field.
stringField :: String -> Field
stringField = flip simpleField [t| String |]

-- | An alias for an optional string field.
optionalStringField :: String -> Field
optionalStringField = optionalField . stringField

-- | An alias for an optional non-empty string field.
optionalNEStringField :: String -> Field
optionalNEStringField = optionalField . flip simpleField [t| NonEmptyString |]

-- | Function to force a non-negative value, without returning via a
-- monad. This is needed for, and should be used /only/ in the case of
-- forcing constants. In case the constant is wrong (< 0), this will
-- become a runtime error.
forceNonNeg :: (Num a, Ord a, Show a) => a -> NonNegative a
forceNonNeg i = case mkNonNegative i of
                  Ok n -> n
                  Bad msg -> error msg

-- ** Disks

-- | Disk index type (embedding constraints on the index value via a
-- smart constructor).
newtype DiskIndex = DiskIndex { unDiskIndex :: Int }
  deriving (Show, Eq, Ord)

-- | Smart constructor for 'DiskIndex'.
mkDiskIndex :: (Monad m) => Int -> m DiskIndex
mkDiskIndex i | i >= 0 && i < C.maxDisks = return (DiskIndex i)
              | otherwise = fail $ "Invalid value for disk index '" ++
                            show i ++ "', required between 0 and " ++
                            show C.maxDisks

instance JSON DiskIndex where
  readJSON v = readJSON v >>= mkDiskIndex
  showJSON = showJSON . unDiskIndex

-- ** I* param types

-- | Type holding disk access modes.
$(declareSADT "DiskAccess"
  [ ("DiskReadOnly",  'C.diskRdonly)
  , ("DiskReadWrite", 'C.diskRdwr)
  ])
$(makeJSONInstance ''DiskAccess)

-- | NIC modification definition.
$(buildObject "INicParams" "inic"
  [ optionalField $ simpleField C.inicMac    [t| NonEmptyString |]
  , optionalField $ simpleField C.inicIp     [t| String         |]
  , optionalField $ simpleField C.inicMode   [t| NonEmptyString |]
  , optionalField $ simpleField C.inicLink   [t| NonEmptyString |]
  , optionalField $ simpleField C.inicName   [t| NonEmptyString |]
  , optionalField $ simpleField C.inicVlan   [t| String         |]
  , optionalField $ simpleField C.inicBridge [t| NonEmptyString |]
  ])

-- | Disk modification definition.
$(buildObject "IDiskParams" "idisk"
  [ specialNumericalField 'parseUnitAssumeBinary . optionalField
      $ simpleField C.idiskSize               [t| Int            |]
  , optionalField $ simpleField C.idiskMode   [t| DiskAccess     |]
  , optionalField $ simpleField C.idiskAdopt  [t| NonEmptyString |]
  , optionalField $ simpleField C.idiskVg     [t| NonEmptyString |]
  , optionalField $ simpleField C.idiskMetavg [t| NonEmptyString |]
  , optionalField $ simpleField C.idiskName   [t| NonEmptyString |]
  , optionalField $ simpleField C.idiskProvider [t| NonEmptyString |]
  , optionalField $ simpleField C.idiskSpindles [t| Int          |]
  , andRestArguments "opaque"
  ])

-- | Disk changes type for OpInstanceRecreateDisks. This is a bit
-- strange, because the type in Python is something like Either
-- [DiskIndex] [DiskChanges], but we can't represent the type of an
-- empty list in JSON, so we have to add a custom case for the empty
-- list.
data RecreateDisksInfo
  = RecreateDisksAll
  | RecreateDisksIndices (NonEmpty DiskIndex)
  | RecreateDisksParams (NonEmpty (DiskIndex, IDiskParams))
    deriving (Eq, Show)

readRecreateDisks :: JSValue -> Text.JSON.Result RecreateDisksInfo
readRecreateDisks (JSArray []) = return RecreateDisksAll
readRecreateDisks v =
  case readJSON v::Text.JSON.Result [DiskIndex] of
    Text.JSON.Ok indices -> liftM RecreateDisksIndices (mkNonEmpty indices)
    _ -> case readJSON v::Text.JSON.Result [(DiskIndex, IDiskParams)] of
           Text.JSON.Ok params -> liftM RecreateDisksParams (mkNonEmpty params)
           _ -> fail $ "Can't parse disk information as either list of disk"
                ++ " indices or list of disk parameters; value received:"
                ++ show (pp_value v)

instance JSON RecreateDisksInfo where
  readJSON = readRecreateDisks
  showJSON  RecreateDisksAll            = showJSON ()
  showJSON (RecreateDisksIndices idx)   = showJSON idx
  showJSON (RecreateDisksParams params) = showJSON params

-- | Simple type for old-style ddm changes.
data DdmOldChanges = DdmOldIndex (NonNegative Int)
                   | DdmOldMod DdmSimple
                     deriving (Eq, Show)

readDdmOldChanges :: JSValue -> Text.JSON.Result DdmOldChanges
readDdmOldChanges v =
  case readJSON v::Text.JSON.Result (NonNegative Int) of
    Text.JSON.Ok nn -> return $ DdmOldIndex nn
    _ -> case readJSON v::Text.JSON.Result DdmSimple of
           Text.JSON.Ok ddms -> return $ DdmOldMod ddms
           _ -> fail $ "Can't parse value '" ++ show (pp_value v) ++ "' as"
                ++ " either index or modification"

instance JSON DdmOldChanges where
  showJSON (DdmOldIndex i) = showJSON i
  showJSON (DdmOldMod m)   = showJSON m
  readJSON = readDdmOldChanges

-- | Instance disk or nic modifications.
data SetParamsMods a
  = SetParamsEmpty
  | SetParamsDeprecated (NonEmpty (DdmOldChanges, a))
  | SetParamsNew (NonEmpty (DdmFull, Int, a))
  | SetParamsNewName (NonEmpty (DdmFull, String, a))
    deriving (Eq, Show)

-- | Custom deserialiser for 'SetParamsMods'.
readSetParams :: (JSON a) => JSValue -> Text.JSON.Result (SetParamsMods a)
readSetParams (JSArray []) = return SetParamsEmpty
readSetParams v =
  liftM SetParamsDeprecated (readJSON v)
  `mplus` liftM SetParamsNew (readJSON v)
  `mplus` liftM SetParamsNewName (readJSON v)

instance (JSON a) => JSON (SetParamsMods a) where
  showJSON SetParamsEmpty = showJSON ()
  showJSON (SetParamsDeprecated v) = showJSON v
  showJSON (SetParamsNew v) = showJSON v
  showJSON (SetParamsNewName v) = showJSON v
  readJSON = readSetParams

-- | Custom type for target_node parameter of OpBackupExport, which
-- varies depending on mode. FIXME: this uses an [JSValue] since
-- we don't care about individual rows (just like the Python code
-- tests). But the proper type could be parsed if we wanted.
data ExportTarget = ExportTargetLocal NonEmptyString
                  | ExportTargetRemote [JSValue]
                    deriving (Eq, Show)

-- | Custom reader for 'ExportTarget'.
readExportTarget :: JSValue -> Text.JSON.Result ExportTarget
readExportTarget (JSString s) = liftM ExportTargetLocal $
                                mkNonEmpty (fromJSString s)
readExportTarget (JSArray arr) = return $ ExportTargetRemote arr
readExportTarget v = fail $ "Invalid value received for 'target_node': " ++
                     show (pp_value v)

instance JSON ExportTarget where
  showJSON (ExportTargetLocal s)  = showJSON s
  showJSON (ExportTargetRemote l) = showJSON l
  readJSON = readExportTarget

-- * Common opcode parameters

pDryRun :: Field
pDryRun =
  withDoc "Run checks only, don't execute" .
  optionalField $ booleanField "dry_run"

pDebugLevel :: Field
pDebugLevel =
  withDoc "Debug level" .
  optionalField $ simpleField "debug_level" [t| NonNegative Int |]

pOpPriority :: Field
pOpPriority =
  withDoc "Opcode priority. Note: python uses a separate constant,\
          \ we're using the actual value we know it's the default" .
  defaultField [| OpPrioNormal |] $
  simpleField "priority" [t| OpSubmitPriority |]

pDependencies :: Field
pDependencies =
  withDoc "Job dependencies" .
  optionalNullSerField $ simpleField "depends" [t| [JobDependency] |]

pComment :: Field
pComment =
  withDoc "Comment field" .
  optionalNullSerField $ stringField "comment"

pReason :: Field
pReason =
  withDoc "Reason trail field" $
  simpleField C.opcodeReason [t| ReasonTrail |]

-- * Parameters

pDebugSimulateErrors :: Field
pDebugSimulateErrors =
  withDoc "Whether to simulate errors (useful for debugging)" $
  defaultFalse "debug_simulate_errors"

pErrorCodes :: Field
pErrorCodes =
  withDoc "Error codes" $
  defaultFalse "error_codes"

pSkipChecks :: Field
pSkipChecks =
  withDoc "Which checks to skip" .
  defaultField [| emptyListSet |] $
  simpleField "skip_checks" [t| ListSet VerifyOptionalChecks |]

pIgnoreErrors :: Field
pIgnoreErrors =
  withDoc "List of error codes that should be treated as warnings" .
  defaultField [| emptyListSet |] $
  simpleField "ignore_errors" [t| ListSet CVErrorCode |]

pVerbose :: Field
pVerbose =
  withDoc "Verbose mode" $
  defaultFalse "verbose"

pOptGroupName :: Field
pOptGroupName =
  withDoc "Optional group name" .
  renameField "OptGroupName" .
  optionalField $ simpleField "group_name" [t| NonEmptyString |]

pGroupName :: Field
pGroupName =
  withDoc "Group name" $
  simpleField "group_name" [t| NonEmptyString |]

-- | Whether to hotplug device.
pHotplug :: Field
pHotplug = defaultFalse "hotplug"

pHotplugIfPossible :: Field
pHotplugIfPossible = defaultFalse "hotplug_if_possible"

pInstances :: Field
pInstances =
  withDoc "List of instances" .
  defaultField [| [] |] $
  simpleField "instances" [t| [NonEmptyString] |]

pOutputFields :: Field
pOutputFields =
  withDoc "Selected output fields" $
  simpleField "output_fields" [t| [NonEmptyString] |]

pName :: Field
pName =
  withDoc "A generic name" $
  simpleField "name" [t| NonEmptyString |]

pForce :: Field
pForce =
  withDoc "Whether to force the operation" $
  defaultFalse "force"

pHvState :: Field
pHvState =
  withDoc "Set hypervisor states" .
  optionalField $ simpleField "hv_state" [t| JSObject JSValue |]

pDiskState :: Field
pDiskState =
  withDoc "Set disk states" .
  optionalField $ simpleField "disk_state" [t| JSObject JSValue |]

-- | Cluster-wide default directory for storing file-backed disks.
pClusterFileStorageDir :: Field
pClusterFileStorageDir =
  renameField "ClusterFileStorageDir" $
  optionalStringField "file_storage_dir"

-- | Cluster-wide default directory for storing shared-file-backed disks.
pClusterSharedFileStorageDir :: Field
pClusterSharedFileStorageDir =
  renameField "ClusterSharedFileStorageDir" $
  optionalStringField "shared_file_storage_dir"

-- | Cluster-wide default directory for storing Gluster-backed disks.
pClusterGlusterStorageDir :: Field
pClusterGlusterStorageDir =
  renameField "ClusterGlusterStorageDir" $
  optionalStringField "gluster_storage_dir"

pInstanceCommunicationNetwork :: Field
pInstanceCommunicationNetwork =
  optionalStringField "instance_communication_network"

-- | The OS to use when zeroing instance disks
pZeroingImage :: Field
pZeroingImage =
  optionalStringField "zeroing_image"

-- | Volume group name.
pVgName :: Field
pVgName =
  withDoc "Volume group name" $
  optionalStringField "vg_name"

pEnabledHypervisors :: Field
pEnabledHypervisors =
  withDoc "List of enabled hypervisors" .
  optionalField $
  simpleField "enabled_hypervisors" [t| [Hypervisor] |]

pClusterHvParams :: Field
pClusterHvParams =
  withDoc "Cluster-wide hypervisor parameters, hypervisor-dependent" .
  renameField "ClusterHvParams" .
  optionalField $
  simpleField "hvparams" [t| GenericContainer String (JSObject JSValue) |]

pClusterBeParams :: Field
pClusterBeParams =
  withDoc "Cluster-wide backend parameter defaults" .
  renameField "ClusterBeParams" .
  optionalField $ simpleField "beparams" [t| JSObject JSValue |]

pOsHvp :: Field
pOsHvp =
  withDoc "Cluster-wide per-OS hypervisor parameter defaults" .
  optionalField $
  simpleField "os_hvp" [t| GenericContainer String (JSObject JSValue) |]

pClusterOsParams :: Field
pClusterOsParams =
  withDoc "Cluster-wide OS parameter defaults" .
  renameField "ClusterOsParams" .
  optionalField $
  simpleField "osparams" [t| GenericContainer String (JSObject JSValue) |]

pClusterOsParamsPrivate :: Field
pClusterOsParamsPrivate =
  withDoc "Cluster-wide private OS parameter defaults" .
  renameField "ClusterOsParamsPrivate" .
  optionalField $
  -- This field needs an unique name to aid Python deserialization
  simpleField "osparams_private_cluster"
    [t| GenericContainer String (JSObject (Private JSValue)) |]

pDiskParams :: Field
pDiskParams =
  withDoc "Disk templates' parameter defaults" .
  optionalField $
  simpleField "diskparams"
              [t| GenericContainer DiskTemplate (JSObject JSValue) |]

pCandidatePoolSize :: Field
pCandidatePoolSize =
  withDoc "Master candidate pool size" .
  optionalField $ simpleField "candidate_pool_size" [t| Positive Int |]

pMaxRunningJobs :: Field
pMaxRunningJobs =
  withDoc "Maximal number of jobs to run simultaneously" .
  optionalField $ simpleField "max_running_jobs" [t| Positive Int |]

pUidPool :: Field
pUidPool =
  withDoc "Set UID pool, must be list of lists describing UID ranges\
          \ (two items, start and end inclusive)" .
  optionalField $ simpleField "uid_pool" [t| [(Int, Int)] |]

pAddUids :: Field
pAddUids =
  withDoc "Extend UID pool, must be list of lists describing UID\
          \ ranges (two items, start and end inclusive)" .
  optionalField $ simpleField "add_uids" [t| [(Int, Int)] |]

pRemoveUids :: Field
pRemoveUids =
  withDoc "Shrink UID pool, must be list of lists describing UID\
          \ ranges (two items, start and end inclusive) to be removed" .
  optionalField $ simpleField "remove_uids" [t| [(Int, Int)] |]

pMaintainNodeHealth :: Field
pMaintainNodeHealth =
  withDoc "Whether to automatically maintain node health" .
  optionalField $ booleanField "maintain_node_health"

-- | Whether to modify and keep in sync the @/etc/hosts@ files of nodes.
pModifyEtcHosts :: Field
pModifyEtcHosts = optionalField $ booleanField "modify_etc_hosts"

-- | Whether to wipe disks before allocating them to instances.
pPreallocWipeDisks :: Field
pPreallocWipeDisks =
  withDoc "Whether to wipe disks before allocating them to instances" .
  optionalField $ booleanField "prealloc_wipe_disks"

pNicParams :: Field
pNicParams =
  withDoc "Cluster-wide NIC parameter defaults" .
  optionalField $ simpleField "nicparams" [t| INicParams |]

pIpolicy :: Field
pIpolicy =
  withDoc "Ipolicy specs" .
  optionalField $ simpleField "ipolicy" [t| JSObject JSValue |]

pDrbdHelper :: Field
pDrbdHelper =
  withDoc "DRBD helper program" $
  optionalStringField "drbd_helper"

pDefaultIAllocator :: Field
pDefaultIAllocator =
  withDoc "Default iallocator for cluster" $
  optionalStringField "default_iallocator"

pDefaultIAllocatorParams :: Field
pDefaultIAllocatorParams =
  withDoc "Default iallocator parameters for cluster" . optionalField
    $ simpleField "default_iallocator_params" [t| JSObject JSValue |]

pMasterNetdev :: Field
pMasterNetdev =
  withDoc "Master network device" $
  optionalStringField "master_netdev"

pMasterNetmask :: Field
pMasterNetmask =
  withDoc "Netmask of the master IP" .
  optionalField $ simpleField "master_netmask" [t| NonNegative Int |]

pReservedLvs :: Field
pReservedLvs =
  withDoc "List of reserved LVs" .
  optionalField $ simpleField "reserved_lvs" [t| [NonEmptyString] |]

pHiddenOs :: Field
pHiddenOs =
  withDoc "Modify list of hidden operating systems: each modification\
          \ must have two items, the operation and the OS name; the operation\
          \ can be add or remove" .
  optionalField $ simpleField "hidden_os" [t| [(DdmSimple, NonEmptyString)] |]

pBlacklistedOs :: Field
pBlacklistedOs =
  withDoc "Modify list of blacklisted operating systems: each\
          \ modification must have two items, the operation and the OS name;\
          \ the operation can be add or remove" .
  optionalField $
  simpleField "blacklisted_os" [t| [(DdmSimple, NonEmptyString)] |]

pUseExternalMipScript :: Field
pUseExternalMipScript =
  withDoc "Whether to use an external master IP address setup script" .
  optionalField $ booleanField "use_external_mip_script"

pEnabledDiskTemplates :: Field
pEnabledDiskTemplates =
  withDoc "List of enabled disk templates" .
  optionalField $
  simpleField "enabled_disk_templates" [t| [DiskTemplate] |]

pQueryWhat :: Field
pQueryWhat =
  withDoc "Resource(s) to query for" $
  simpleField "what" [t| Qlang.QueryTypeOp |]

pUseLocking :: Field
pUseLocking =
  withDoc "Whether to use synchronization" $
  defaultFalse "use_locking"

pQueryFields :: Field
pQueryFields =
  withDoc "Requested fields" $
  simpleField "fields" [t| [NonEmptyString] |]

pQueryFilter :: Field
pQueryFilter =
  withDoc "Query filter" .
  optionalField $ simpleField "qfilter" [t| [JSValue] |]

pQueryFieldsFields :: Field
pQueryFieldsFields =
  withDoc "Requested fields; if not given, all are returned" .
  renameField "QueryFieldsFields" $
  optionalField pQueryFields

pNodeNames :: Field
pNodeNames =
  withDoc "List of node names to run the OOB command against" .
  defaultField [| [] |] $ simpleField "node_names" [t| [NonEmptyString] |]

pNodeUuids :: Field
pNodeUuids =
  withDoc "List of node UUIDs" .
  optionalField $ simpleField "node_uuids" [t| [NonEmptyString] |]

pOobCommand :: Field
pOobCommand =
  withDoc "OOB command to run" $
  simpleField "command" [t| OobCommand |]

pOobTimeout :: Field
pOobTimeout =
  withDoc "Timeout before the OOB helper will be terminated" .
  defaultField [| C.oobTimeout |] $
  simpleField "timeout" [t| Int |]

pIgnoreStatus :: Field
pIgnoreStatus =
  withDoc "Ignores the node offline status for power off" $
  defaultFalse "ignore_status"

pPowerDelay :: Field
pPowerDelay =
  -- FIXME: we can't use the proper type "NonNegative Double", since
  -- the default constant is a plain Double, not a non-negative one.
  -- And trying to fix the constant introduces a cyclic import.
  withDoc "Time in seconds to wait between powering on nodes" .
  defaultField [| C.oobPowerDelay |] $
  simpleField "power_delay" [t| Double |]

pRequiredNodes :: Field
pRequiredNodes =
  withDoc "Required list of node names" .
  renameField "ReqNodes " $ simpleField "nodes" [t| [NonEmptyString] |]

pRequiredNodeUuids :: Field
pRequiredNodeUuids =
  withDoc "Required list of node UUIDs" .
  renameField "ReqNodeUuids " . optionalField $
  simpleField "node_uuids" [t| [NonEmptyString] |]

pRestrictedCommand :: Field
pRestrictedCommand =
  withDoc "Restricted command name" .
  renameField "RestrictedCommand" $
  simpleField "command" [t| NonEmptyString |]

pNodeName :: Field
pNodeName =
  withDoc "A required node name (for single-node LUs)" $
  simpleField "node_name" [t| NonEmptyString |]

pNodeUuid :: Field
pNodeUuid =
  withDoc "A node UUID (for single-node LUs)" .
  optionalField $ simpleField "node_uuid" [t| NonEmptyString |]

pPrimaryIp :: Field
pPrimaryIp =
  withDoc "Primary IP address" .
  optionalField $
  simpleField "primary_ip" [t| NonEmptyString |]

pSecondaryIp :: Field
pSecondaryIp =
  withDoc "Secondary IP address" $
  optionalNEStringField "secondary_ip"

pReadd :: Field
pReadd =
  withDoc "Whether node is re-added to cluster" $
  defaultFalse "readd"

pNodeGroup :: Field
pNodeGroup =
  withDoc "Initial node group" $
  optionalNEStringField "group"

pMasterCapable :: Field
pMasterCapable =
  withDoc "Whether node can become master or master candidate" .
  optionalField $ booleanField "master_capable"

pVmCapable :: Field
pVmCapable =
  withDoc "Whether node can host instances" .
  optionalField $ booleanField "vm_capable"

pNdParams :: Field
pNdParams =
  withDoc "Node parameters" .
  renameField "genericNdParams" .
  optionalField $ simpleField "ndparams" [t| JSObject JSValue |]

pNames :: Field
pNames =
  withDoc "List of names" .
  defaultField [| [] |] $ simpleField "names" [t| [NonEmptyString] |]

pNodes :: Field
pNodes =
  withDoc "List of nodes" .
  defaultField [| [] |] $ simpleField "nodes" [t| [NonEmptyString] |]

pStorageType :: Field
pStorageType =
  withDoc "Storage type" $ simpleField "storage_type" [t| StorageType |]

pOptStorageType :: Field
pOptStorageType =
  withDoc "Storage type" .
  renameField "OptStorageType" .
  optionalField $ simpleField "storage_type" [t| StorageType |]

pStorageName :: Field
pStorageName =
  withDoc "Storage name" .
  renameField "StorageName" .
  optionalField $ simpleField "name" [t| NonEmptyString |]

pStorageChanges :: Field
pStorageChanges =
  withDoc "Requested storage changes" $
  simpleField "changes" [t| JSObject JSValue |]

pIgnoreConsistency :: Field
pIgnoreConsistency =
  withDoc "Whether to ignore disk consistency" $
  defaultFalse "ignore_consistency"

pMasterCandidate :: Field
pMasterCandidate =
  withDoc "Whether the node should become a master candidate" .
  optionalField $ booleanField "master_candidate"

pOffline :: Field
pOffline =
  withDoc "Whether to mark the node or instance offline" .
  optionalField $ booleanField "offline"

pDrained ::Field
pDrained =
  withDoc "Whether to mark the node as drained" .
  optionalField $ booleanField "drained"

pAutoPromote :: Field
pAutoPromote =
  withDoc "Whether node(s) should be promoted to master candidate if\
          \ necessary" $
  defaultFalse "auto_promote"

pPowered :: Field
pPowered =
  withDoc "Whether the node should be marked as powered" .
  optionalField $ booleanField "powered"

pMigrationMode :: Field
pMigrationMode =
  withDoc "Migration type (live/non-live)" .
  renameField "MigrationMode" .
  optionalField $
  simpleField "mode" [t| MigrationMode |]

pMigrationLive :: Field
pMigrationLive =
  withDoc "Obsolete \'live\' migration mode (do not use)" .
  renameField "OldLiveMode" . optionalField $ booleanField "live"

pMigrationTargetNode :: Field
pMigrationTargetNode =
  withDoc "Target node for instance migration/failover" $
  optionalNEStringField "target_node"

pMigrationTargetNodeUuid :: Field
pMigrationTargetNodeUuid =
  withDoc "Target node UUID for instance migration/failover" $
  optionalNEStringField "target_node_uuid"

pAllowRuntimeChgs :: Field
pAllowRuntimeChgs =
  withDoc "Whether to allow runtime changes while migrating" $
  defaultTrue "allow_runtime_changes"

pIgnoreIpolicy :: Field
pIgnoreIpolicy =
  withDoc "Whether to ignore ipolicy violations" $
  defaultFalse "ignore_ipolicy"

pIallocator :: Field
pIallocator =
  withDoc "Iallocator for deciding the target node for shared-storage\
          \ instances" $
  optionalNEStringField "iallocator"

pEarlyRelease :: Field
pEarlyRelease =
  withDoc "Whether to release locks as soon as possible" $
  defaultFalse "early_release"

pRemoteNode :: Field
pRemoteNode =
  withDoc "New secondary node" $
  optionalNEStringField "remote_node"

pRemoteNodeUuid :: Field
pRemoteNodeUuid =
  withDoc "New secondary node UUID" $
  optionalNEStringField "remote_node_uuid"

pEvacMode :: Field
pEvacMode =
  withDoc "Node evacuation mode" .
  renameField "EvacMode" $ simpleField "mode" [t| EvacMode |]

pInstanceName :: Field
pInstanceName =
  withDoc "A required instance name (for single-instance LUs)" $
  simpleField "instance_name" [t| String |]

pInstanceCommunication :: Field
pInstanceCommunication =
  withDoc C.instanceCommunicationDoc $
  defaultFalse "instance_communication"

pOptInstanceCommunication :: Field
pOptInstanceCommunication =
  withDoc C.instanceCommunicationDoc .
  renameField "OptInstanceCommunication" .
  optionalField $
  booleanField "instance_communication"

pForceVariant :: Field
pForceVariant =
  withDoc "Whether to force an unknown OS variant" $
  defaultFalse "force_variant"

pWaitForSync :: Field
pWaitForSync =
  withDoc "Whether to wait for the disk to synchronize" $
  defaultTrue "wait_for_sync"

pNameCheck :: Field
pNameCheck =
  withDoc "Whether to check name" $
  defaultTrue "name_check"

pInstBeParams :: Field
pInstBeParams =
  withDoc "Backend parameters for instance" .
  renameField "InstBeParams" .
  defaultField [| toJSObject [] |] $
  simpleField "beparams" [t| JSObject JSValue |]

pInstDisks :: Field
pInstDisks =
  withDoc "List of instance disks" .
  renameField "instDisks" $ simpleField "disks" [t| [IDiskParams] |]

pDiskTemplate :: Field
pDiskTemplate =
  withDoc "Disk template" $
  simpleField "disk_template" [t| DiskTemplate |]

pFileDriver :: Field
pFileDriver =
  withDoc "Driver for file-backed disks" .
  optionalField $ simpleField "file_driver" [t| FileDriver |]

pFileStorageDir :: Field
pFileStorageDir =
  withDoc "Directory for storing file-backed disks" $
  optionalNEStringField "file_storage_dir"

pInstHvParams :: Field
pInstHvParams =
  withDoc "Hypervisor parameters for instance, hypervisor-dependent" .
  renameField "InstHvParams" .
  defaultField [| toJSObject [] |] $
  simpleField "hvparams" [t| JSObject JSValue |]

pHypervisor :: Field
pHypervisor =
  withDoc "Selected hypervisor for an instance" .
  optionalField $
  simpleField "hypervisor" [t| Hypervisor |]

pResetDefaults :: Field
pResetDefaults =
  withDoc "Reset instance parameters to default if equal" $
  defaultFalse "identify_defaults"

pIpCheck :: Field
pIpCheck =
  withDoc "Whether to ensure instance's IP address is inactive" $
  defaultTrue "ip_check"

pIpConflictsCheck :: Field
pIpConflictsCheck =
  withDoc "Whether to check for conflicting IP addresses" $
  defaultTrue "conflicts_check"

pInstCreateMode :: Field
pInstCreateMode =
  withDoc "Instance creation mode" .
  renameField "InstCreateMode" $ simpleField "mode" [t| InstCreateMode |]

pInstNics :: Field
pInstNics =
  withDoc "List of NIC (network interface) definitions" $
  simpleField "nics" [t| [INicParams] |]

pNoInstall :: Field
pNoInstall =
  withDoc "Do not install the OS (will disable automatic start)" .
  optionalField $ booleanField "no_install"

pInstOs :: Field
pInstOs =
  withDoc "OS type for instance installation" $
  optionalNEStringField "os_type"

pInstOsParams :: Field
pInstOsParams =
  withDoc "OS parameters for instance" .
  renameField "InstOsParams" .
  defaultField [| toJSObject [] |] $
  simpleField "osparams" [t| JSObject JSValue |]

pInstOsParamsPrivate :: Field
pInstOsParamsPrivate =
  withDoc "Private OS parameters for instance" .
  optionalField $
  simpleField "osparams_private" [t| JSObject (Private JSValue) |]

pInstOsParamsSecret :: Field
pInstOsParamsSecret =
  withDoc "Secret OS parameters for instance" .
  optionalField $
  simpleField "osparams_secret" [t| JSObject (Private JSValue) |]

pPrimaryNode :: Field
pPrimaryNode =
  withDoc "Primary node for an instance" $
  optionalNEStringField "pnode"

pPrimaryNodeUuid :: Field
pPrimaryNodeUuid =
  withDoc "Primary node UUID for an instance" $
  optionalNEStringField "pnode_uuid"

pSecondaryNode :: Field
pSecondaryNode =
  withDoc "Secondary node for an instance" $
  optionalNEStringField "snode"

pSecondaryNodeUuid :: Field
pSecondaryNodeUuid =
  withDoc "Secondary node UUID for an instance" $
  optionalNEStringField "snode_uuid"

pSourceHandshake :: Field
pSourceHandshake =
  withDoc "Signed handshake from source (remote import only)" .
  optionalField $ simpleField "source_handshake" [t| [JSValue] |]

pSourceInstance :: Field
pSourceInstance =
  withDoc "Source instance name (remote import only)" $
  optionalNEStringField "source_instance_name"

-- FIXME: non-negative int, whereas the constant is a plain int.
pSourceShutdownTimeout :: Field
pSourceShutdownTimeout =
  withDoc "How long source instance was given to shut down (remote import\
          \ only)" .
  defaultField [| forceNonNeg C.defaultShutdownTimeout |] $
  simpleField "source_shutdown_timeout" [t| NonNegative Int |]

pSourceX509Ca :: Field
pSourceX509Ca =
  withDoc "Source X509 CA in PEM format (remote import only)" $
  optionalNEStringField "source_x509_ca"

pSrcNode :: Field
pSrcNode =
  withDoc "Source node for import" $
  optionalNEStringField "src_node"

pSrcNodeUuid :: Field
pSrcNodeUuid =
  withDoc "Source node UUID for import" $
  optionalNEStringField "src_node_uuid"

pSrcPath :: Field
pSrcPath =
  withDoc "Source directory for import" $
  optionalNEStringField "src_path"

pStartInstance :: Field
pStartInstance =
  withDoc "Whether to start instance after creation" $
  defaultTrue "start"

-- FIXME: unify/simplify with pTags, once that migrates to NonEmpty String"
pInstTags :: Field
pInstTags =
  withDoc "Instance tags" .
  renameField "InstTags" .
  defaultField [| [] |] $
  simpleField "tags" [t| [NonEmptyString] |]

pMultiAllocInstances :: Field
pMultiAllocInstances =
  withDoc "List of instance create opcodes describing the instances to\
          \ allocate" .
  renameField "InstMultiAlloc" .
  defaultField [| [] |] $
  simpleField "instances"[t| [JSValue] |]

pOpportunisticLocking :: Field
pOpportunisticLocking =
  withDoc "Whether to employ opportunistic locking for nodes, meaning\
          \ nodes already locked by another opcode won't be considered for\
          \ instance allocation (only when an iallocator is used)" $
  defaultFalse "opportunistic_locking"

pInstanceUuid :: Field
pInstanceUuid =
  withDoc "An instance UUID (for single-instance LUs)" .
  optionalField $ simpleField "instance_uuid" [t| NonEmptyString |]

pTempOsParams :: Field
pTempOsParams =
  withDoc "Temporary OS parameters (currently only in reinstall, might be\
          \ added to install as well)" .
  renameField "TempOsParams" .
  optionalField $ simpleField "osparams" [t| JSObject JSValue |]

pTempOsParamsPrivate :: Field
pTempOsParamsPrivate =
  withDoc "Private OS parameters for instance reinstalls" .
  optionalField $
  simpleField "osparams_private" [t| JSObject (Private JSValue) |]

pTempOsParamsSecret :: Field
pTempOsParamsSecret =
  withDoc "Secret OS parameters for instance reinstalls" .
  optionalField $
  simpleField "osparams_secret" [t| JSObject (Private JSValue) |]

pShutdownTimeout :: Field
pShutdownTimeout =
  withDoc "How long to wait for instance to shut down" .
  defaultField [| forceNonNeg C.defaultShutdownTimeout |] $
  simpleField "shutdown_timeout" [t| NonNegative Int |]

-- | Another name for the shutdown timeout, because we like to be
-- inconsistent.
pShutdownTimeout' :: Field
pShutdownTimeout' =
  withDoc "How long to wait for instance to shut down" .
  renameField "InstShutdownTimeout" .
  defaultField [| forceNonNeg C.defaultShutdownTimeout |] $
  simpleField "timeout" [t| NonNegative Int |]

pIgnoreFailures :: Field
pIgnoreFailures =
  withDoc "Whether to ignore failures during removal" $
  defaultFalse "ignore_failures"

pNewName :: Field
pNewName =
  withDoc "New group or instance name" $
  simpleField "new_name" [t| NonEmptyString |]

pIgnoreOfflineNodes :: Field
pIgnoreOfflineNodes =
  withDoc "Whether to ignore offline nodes" $
  defaultFalse "ignore_offline_nodes"

pTempHvParams :: Field
pTempHvParams =
  withDoc "Temporary hypervisor parameters, hypervisor-dependent" .
  renameField "TempHvParams" .
  defaultField [| toJSObject [] |] $
  simpleField "hvparams" [t| JSObject JSValue |]

pTempBeParams :: Field
pTempBeParams =
  withDoc "Temporary backend parameters" .
  renameField "TempBeParams" .
  defaultField [| toJSObject [] |] $
  simpleField "beparams" [t| JSObject JSValue |]

pNoRemember :: Field
pNoRemember =
  withDoc "Do not remember instance state changes" $
  defaultFalse "no_remember"

pStartupPaused :: Field
pStartupPaused =
  withDoc "Pause instance at startup" $
  defaultFalse "startup_paused"

pIgnoreSecondaries :: Field
pIgnoreSecondaries =
  withDoc "Whether to start the instance even if secondary disks are failing" $
  defaultFalse "ignore_secondaries"

pRebootType :: Field
pRebootType =
  withDoc "How to reboot the instance" $
  simpleField "reboot_type" [t| RebootType |]

pReplaceDisksMode :: Field
pReplaceDisksMode =
  withDoc "Replacement mode" .
  renameField "ReplaceDisksMode" $ simpleField "mode" [t| ReplaceDisksMode |]

pReplaceDisksList :: Field
pReplaceDisksList =
  withDoc "List of disk indices" .
  renameField "ReplaceDisksList" .
  defaultField [| [] |] $
  simpleField "disks" [t| [DiskIndex] |]

pMigrationCleanup :: Field
pMigrationCleanup =
  withDoc "Whether a previously failed migration should be cleaned up" .
  renameField "MigrationCleanup" $ defaultFalse "cleanup"

pAllowFailover :: Field
pAllowFailover =
  withDoc "Whether we can fallback to failover if migration is not possible" $
  defaultFalse "allow_failover"

pMoveTargetNode :: Field
pMoveTargetNode =
  withDoc "Target node for instance move" .
  renameField "MoveTargetNode" $
  simpleField "target_node" [t| NonEmptyString |]

pMoveTargetNodeUuid :: Field
pMoveTargetNodeUuid =
  withDoc "Target node UUID for instance move" .
  renameField "MoveTargetNodeUuid" . optionalField $
  simpleField "target_node_uuid" [t| NonEmptyString |]

pMoveCompress :: Field
pMoveCompress =
  withDoc "Compression mode to use during instance moves" .
  defaultField [| None |] $
  simpleField "compress" [t| ImportExportCompression |]

pBackupCompress :: Field
pBackupCompress =
  withDoc "Compression mode to use for moves during backups/imports" .
  defaultField [| None |] $
  simpleField "compress" [t| ImportExportCompression |]

pIgnoreDiskSize :: Field
pIgnoreDiskSize =
  withDoc "Whether to ignore recorded disk size" $
  defaultFalse "ignore_size"

pWaitForSyncFalse :: Field
pWaitForSyncFalse =
  withDoc "Whether to wait for the disk to synchronize (defaults to false)" $
  defaultField [| False |] pWaitForSync

pRecreateDisksInfo :: Field
pRecreateDisksInfo =
  withDoc "Disk list for recreate disks" .
  renameField "RecreateDisksInfo" .
  defaultField [| RecreateDisksAll |] $
  simpleField "disks" [t| RecreateDisksInfo |]

pStatic :: Field
pStatic =
  withDoc "Whether to only return configuration data without querying nodes" $
  defaultFalse "static"

pInstParamsNicChanges :: Field
pInstParamsNicChanges =
  withDoc "List of NIC changes" .
  renameField "InstNicChanges" .
  defaultField [| SetParamsEmpty |] $
  simpleField "nics" [t| SetParamsMods INicParams |]

pInstParamsDiskChanges :: Field
pInstParamsDiskChanges =
  withDoc "List of disk changes" .
  renameField "InstDiskChanges" .
  defaultField [| SetParamsEmpty |] $
  simpleField "disks" [t| SetParamsMods IDiskParams |]

pRuntimeMem :: Field
pRuntimeMem =
  withDoc "New runtime memory" .
  optionalField $ simpleField "runtime_mem" [t| Positive Int |]

pOptDiskTemplate :: Field
pOptDiskTemplate =
  withDoc "Instance disk template" .
  optionalField .
  renameField "OptDiskTemplate" $
  simpleField "disk_template" [t| DiskTemplate |]

pOsNameChange :: Field
pOsNameChange =
  withDoc "Change the instance's OS without reinstalling the instance" $
  optionalNEStringField "os_name"

pDiskIndex :: Field
pDiskIndex =
  withDoc "Disk index for e.g. grow disk" .
  renameField "DiskIndex " $ simpleField "disk" [t| DiskIndex |]

pDiskChgAmount :: Field
pDiskChgAmount =
  withDoc "Disk amount to add or grow to" .
  renameField "DiskChgAmount" $ simpleField "amount" [t| NonNegative Int |]

pDiskChgAbsolute :: Field
pDiskChgAbsolute =
  withDoc
    "Whether the amount parameter is an absolute target or a relative one" .
  renameField "DiskChkAbsolute" $ defaultFalse "absolute"

pTargetGroups :: Field
pTargetGroups =
  withDoc
    "Destination group names or UUIDs (defaults to \"all but current group\")" .
  optionalField $ simpleField "target_groups" [t| [NonEmptyString] |]

pNodeGroupAllocPolicy :: Field
pNodeGroupAllocPolicy =
  withDoc "Instance allocation policy" .
  optionalField $
  simpleField "alloc_policy" [t| AllocPolicy |]

pGroupNodeParams :: Field
pGroupNodeParams =
  withDoc "Default node parameters for group" .
  optionalField $ simpleField "ndparams" [t| JSObject JSValue |]

pExportMode :: Field
pExportMode =
  withDoc "Export mode" .
  renameField "ExportMode" $ simpleField "mode" [t| ExportMode |]

-- FIXME: Rename target_node as it changes meaning for different
-- export modes (e.g. "destination")
pExportTargetNode :: Field
pExportTargetNode =
  withDoc "Target node (depends on export mode)" .
  renameField "ExportTarget" $
  simpleField "target_node" [t| ExportTarget |]

pExportTargetNodeUuid :: Field
pExportTargetNodeUuid =
  withDoc "Target node UUID (if local export)" .
  renameField "ExportTargetNodeUuid" . optionalField $
  simpleField "target_node_uuid" [t| NonEmptyString |]

pShutdownInstance :: Field
pShutdownInstance =
  withDoc "Whether to shutdown the instance before export" $
  defaultTrue "shutdown"

pRemoveInstance :: Field
pRemoveInstance =
  withDoc "Whether to remove instance after export" $
  defaultFalse "remove_instance"

pIgnoreRemoveFailures :: Field
pIgnoreRemoveFailures =
  withDoc "Whether to ignore failures while removing instances" $
  defaultFalse "ignore_remove_failures"

pX509KeyName :: Field
pX509KeyName =
  withDoc "Name of X509 key (remote export only)" .
  optionalField $ simpleField "x509_key_name" [t| [JSValue] |]

pX509DestCA :: Field
pX509DestCA =
  withDoc "Destination X509 CA (remote export only)" $
  optionalNEStringField "destination_x509_ca"

pZeroFreeSpace :: Field
pZeroFreeSpace =
  withDoc "Whether to zero the free space on the disks of the instance" $
  defaultFalse "zero_free_space"

pZeroingTimeoutFixed :: Field
pZeroingTimeoutFixed =
  withDoc "The fixed part of time to wait before declaring the zeroing\
           \ operation to have failed" .
  optionalField $ simpleField "zeroing_timeout_fixed" [t| Int |]

pZeroingTimeoutPerMiB :: Field
pZeroingTimeoutPerMiB =
  withDoc "The variable part of time to wait before declaring the zeroing\
           \ operation to have failed, dependent on total size of disks" .
  optionalField $ simpleField "zeroing_timeout_per_mib" [t| Double |]

pTagsObject :: Field
pTagsObject =
  withDoc "Tag kind" $
  simpleField "kind" [t| TagKind |]

pTagsName :: Field
pTagsName =
  withDoc "Name of object" .
  renameField "TagsGetName" .
  optionalField $ simpleField "name" [t| String |]

pTagsList :: Field
pTagsList =
  withDoc "List of tag names" $
  simpleField "tags" [t| [String] |]

-- FIXME: this should be compiled at load time?
pTagSearchPattern :: Field
pTagSearchPattern =
  withDoc "Search pattern (regular expression)" .
  renameField "TagSearchPattern" $
  simpleField "pattern" [t| NonEmptyString |]

pDelayDuration :: Field
pDelayDuration =
  withDoc "Duration parameter for 'OpTestDelay'" .
  renameField "DelayDuration" $
  simpleField "duration" [t| Double |]

pDelayOnMaster :: Field
pDelayOnMaster =
  withDoc "on_master field for 'OpTestDelay'" .
  renameField "DelayOnMaster" $
  defaultTrue "on_master"

pDelayOnNodes :: Field
pDelayOnNodes =
  withDoc "on_nodes field for 'OpTestDelay'" .
  renameField "DelayOnNodes" .
  defaultField [| [] |] $
  simpleField "on_nodes" [t| [NonEmptyString] |]

pDelayOnNodeUuids :: Field
pDelayOnNodeUuids =
  withDoc "on_node_uuids field for 'OpTestDelay'" .
  renameField "DelayOnNodeUuids" . optionalField $
  simpleField "on_node_uuids" [t| [NonEmptyString] |]

pDelayRepeat :: Field
pDelayRepeat =
  withDoc "Repeat parameter for OpTestDelay" .
  renameField "DelayRepeat" .
  defaultField [| forceNonNeg (0::Int) |] $
  simpleField "repeat" [t| NonNegative Int |]

pDelayInterruptible :: Field
pDelayInterruptible =
  withDoc "Allows socket-based interruption of a running OpTestDelay" .
  renameField "DelayInterruptible" .
  defaultField [| False |] $
  simpleField "interruptible" [t| Bool |]

pIAllocatorDirection :: Field
pIAllocatorDirection =
  withDoc "IAllocator test direction" .
  renameField "IAllocatorDirection" $
  simpleField "direction" [t| IAllocatorTestDir |]

pIAllocatorMode :: Field
pIAllocatorMode =
  withDoc "IAllocator test mode" .
  renameField "IAllocatorMode" $
  simpleField "mode" [t| IAllocatorMode |]

pIAllocatorReqName :: Field
pIAllocatorReqName =
  withDoc "IAllocator target name (new instance, node to evac, etc.)" .
  renameField "IAllocatorReqName" $ simpleField "name" [t| NonEmptyString |]

pIAllocatorNics :: Field
pIAllocatorNics =
  withDoc "Custom OpTestIAllocator nics" .
  renameField "IAllocatorNics" .
  optionalField $ simpleField "nics" [t| [INicParams] |]

pIAllocatorDisks :: Field
pIAllocatorDisks =
  withDoc "Custom OpTestAllocator disks" .
  renameField "IAllocatorDisks" .
  optionalField $ simpleField "disks" [t| [JSValue] |]

pIAllocatorMemory :: Field
pIAllocatorMemory =
  withDoc "IAllocator memory field" .
  renameField "IAllocatorMem" .
  optionalField $
  simpleField "memory" [t| NonNegative Int |]

pIAllocatorVCpus :: Field
pIAllocatorVCpus =
  withDoc "IAllocator vcpus field" .
  renameField "IAllocatorVCpus" .
  optionalField $
  simpleField "vcpus" [t| NonNegative Int |]

pIAllocatorOs :: Field
pIAllocatorOs =
  withDoc "IAllocator os field" .
  renameField "IAllocatorOs" $ optionalNEStringField "os"

pIAllocatorInstances :: Field
pIAllocatorInstances =
  withDoc "IAllocator instances field" .
  renameField "IAllocatorInstances " .
  optionalField $
  simpleField "instances" [t| [NonEmptyString] |]

pIAllocatorEvacMode :: Field
pIAllocatorEvacMode =
  withDoc "IAllocator evac mode" .
  renameField "IAllocatorEvacMode" .
  optionalField $
  simpleField "evac_mode" [t| EvacMode |]

pIAllocatorSpindleUse :: Field
pIAllocatorSpindleUse =
  withDoc "IAllocator spindle use" .
  renameField "IAllocatorSpindleUse" .
  defaultField [| forceNonNeg (1::Int) |] $
  simpleField "spindle_use" [t| NonNegative Int |]

pIAllocatorCount :: Field
pIAllocatorCount =
  withDoc "IAllocator count field" .
  renameField "IAllocatorCount" .
  defaultField [| forceNonNeg (1::Int) |] $
  simpleField "count" [t| NonNegative Int |]

pJQueueNotifyWaitLock :: Field
pJQueueNotifyWaitLock =
  withDoc "'OpTestJqueue' notify_waitlock" $
  defaultFalse "notify_waitlock"

pJQueueNotifyExec :: Field
pJQueueNotifyExec =
  withDoc "'OpTestJQueue' notify_exec" $
  defaultFalse "notify_exec"

pJQueueLogMessages :: Field
pJQueueLogMessages =
  withDoc "'OpTestJQueue' log_messages" .
  defaultField [| [] |] $ simpleField "log_messages" [t| [String] |]

pJQueueFail :: Field
pJQueueFail =
  withDoc "'OpTestJQueue' fail attribute" .
  renameField "JQueueFail" $ defaultFalse "fail"

pTestDummyResult :: Field
pTestDummyResult =
  withDoc "'OpTestDummy' result field" .
  renameField "TestDummyResult" $ simpleField "result" [t| JSValue |]

pTestDummyMessages :: Field
pTestDummyMessages =
  withDoc "'OpTestDummy' messages field" .
  renameField "TestDummyMessages" $
  simpleField "messages" [t| JSValue |]

pTestDummyFail :: Field
pTestDummyFail =
  withDoc "'OpTestDummy' fail field" .
  renameField "TestDummyFail" $ simpleField "fail" [t| JSValue |]

pTestDummySubmitJobs :: Field
pTestDummySubmitJobs =
  withDoc "'OpTestDummy' submit_jobs field" .
  renameField "TestDummySubmitJobs" $
  simpleField "submit_jobs" [t| JSValue |]

pNetworkName :: Field
pNetworkName =
  withDoc "Network name" $
  simpleField "network_name" [t| NonEmptyString |]

pNetworkAddress4 :: Field
pNetworkAddress4 =
  withDoc "Network address (IPv4 subnet)" .
  renameField "NetworkAddress4" $
  simpleField "network" [t| IPv4Network |]

pNetworkGateway4 :: Field
pNetworkGateway4 =
  withDoc "Network gateway (IPv4 address)" .
  renameField "NetworkGateway4" .
  optionalField $ simpleField "gateway" [t| IPv4Address |]

pNetworkAddress6 :: Field
pNetworkAddress6 =
  withDoc "Network address (IPv6 subnet)" .
  renameField "NetworkAddress6" .
  optionalField $ simpleField "network6" [t| IPv6Network |]

pNetworkGateway6 :: Field
pNetworkGateway6 =
  withDoc "Network gateway (IPv6 address)" .
  renameField "NetworkGateway6" .
  optionalField $ simpleField "gateway6" [t| IPv6Address |]

pNetworkMacPrefix :: Field
pNetworkMacPrefix =
  withDoc "Network specific mac prefix (that overrides the cluster one)" .
  renameField "NetMacPrefix" $
  optionalNEStringField "mac_prefix"

pNetworkAddRsvdIps :: Field
pNetworkAddRsvdIps =
  withDoc "Which IP addresses to reserve" .
  renameField "NetworkAddRsvdIps" .
  optionalField $
  simpleField "add_reserved_ips" [t| [IPv4Address] |]

pNetworkRemoveRsvdIps :: Field
pNetworkRemoveRsvdIps =
  withDoc "Which external IP addresses to release" .
  renameField "NetworkRemoveRsvdIps" .
  optionalField $
  simpleField "remove_reserved_ips" [t| [IPv4Address] |]

pNetworkMode :: Field
pNetworkMode =
  withDoc "Network mode when connecting to a group" $
  simpleField "network_mode" [t| NICMode |]

pNetworkLink :: Field
pNetworkLink =
  withDoc "Network link when connecting to a group" $
  simpleField "network_link" [t| NonEmptyString |]
