1 {-# LANGUAGE TemplateHaskell #-}
3 {-| Implementation of the Ganeti config objects.
5 Some object fields are not implemented yet, and as such they are
12 Copyright (C) 2011, 2012 Google Inc.
14 This program is free software; you can redistribute it and/or modify
15 it under the terms of the GNU General Public License as published by
16 the Free Software Foundation; either version 2 of the License, or
17 (at your option) any later version.
19 This program is distributed in the hope that it will be useful, but
20 WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 General Public License for more details.
24 You should have received a copy of the GNU General Public License
25 along with this program; if not, write to the Free Software
26 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
36 , PartialNicParams(..)
65 , FilledISpecParams(..)
66 , PartialISpecParams(..)
88 , DictObject(..) -- re-exported from THH
89 , TagSet -- re-exported from THH
93 import Data.List (foldl')
95 import qualified Data.Map as Map
96 import qualified Data.Set as Set
97 import Text.JSON (showJSON, readJSON, JSON, JSValue(..))
98 import qualified Text.JSON as J
100 import qualified Ganeti.Constants as C
105 -- * Generic definitions
107 -- | Fills one map with keys from the other map, if not already
108 -- existing. Mirrors objects.py:FillDict.
109 fillDict :: (Ord k) => Map.Map k v -> Map.Map k v -> [k] -> Map.Map k v
110 fillDict defaults custom skip_keys =
111 let updated = Map.union custom defaults
112 in foldl' (flip Map.delete) updated skip_keys
114 -- | The VTYPES, a mini-type system in Python.
115 $(declareSADT "VType"
116 [ ("VTypeString", 'C.vtypeString)
117 , ("VTypeMaybeString", 'C.vtypeMaybeString)
118 , ("VTypeBool", 'C.vtypeBool)
119 , ("VTypeSize", 'C.vtypeSize)
120 , ("VTypeInt", 'C.vtypeInt)
122 $(makeJSONInstance ''VType)
124 -- | The hypervisor parameter type. This is currently a simple map,
125 -- without type checking on key/value pairs.
126 type HvParams = Container JSValue
128 -- | The OS parameters type. This is, and will remain, a string
129 -- container, since the keys are dynamically declared by the OSes, and
130 -- the values are always strings.
131 type OsParams = Container String
133 -- | Class of objects that have timestamps.
134 class TimeStampObject a where
135 cTimeOf :: a -> Double
136 mTimeOf :: a -> Double
138 -- | Class of objects that have an UUID.
139 class UuidObject a where
140 uuidOf :: a -> String
142 -- | Class of object that have a serial number.
143 class SerialNoObject a where
146 -- | Class of objects that have tags.
147 class TagsObject a where
148 tagsOf :: a -> Set.Set String
150 -- * Node role object
152 $(declareSADT "NodeRole"
153 [ ("NROffline", 'C.nrOffline)
154 , ("NRDrained", 'C.nrDrained)
155 , ("NRRegular", 'C.nrRegular)
156 , ("NRCandidate", 'C.nrMcandidate)
157 , ("NRMaster", 'C.nrMaster)
159 $(makeJSONInstance ''NodeRole)
161 -- | The description of the node role.
162 roleDescription :: NodeRole -> String
163 roleDescription NROffline = "offline"
164 roleDescription NRDrained = "drained"
165 roleDescription NRRegular = "regular"
166 roleDescription NRCandidate = "master candidate"
167 roleDescription NRMaster = "master"
169 -- * Network definitions
171 -- FIXME: Not all types might be correct here, since they
172 -- haven't been exhaustively deduced from the python code yet.
173 $(buildObject "Network" "network" $
174 [ simpleField "name" [t| NonEmptyString |]
176 simpleField "network_type" [t| NetworkType |]
178 simpleField "mac_prefix" [t| String |]
180 simpleField "family" [t| Int |]
181 , simpleField "network" [t| NonEmptyString |]
183 simpleField "network6" [t| String |]
185 simpleField "gateway" [t| String |]
187 simpleField "gateway6" [t| String |]
189 simpleField "size" [t| J.JSValue |]
191 simpleField "reservations" [t| String |]
193 simpleField "ext_reservations" [t| String |]
199 instance SerialNoObject Network where
200 serialOf = networkSerial
202 instance TagsObject Network where
205 instance UuidObject Network where
210 $(buildParam "Nic" "nicp"
211 [ simpleField "mode" [t| NICMode |]
212 , simpleField "link" [t| String |]
215 $(buildObject "PartialNic" "nic"
216 [ simpleField "mac" [t| String |]
217 , optionalField $ simpleField "ip" [t| String |]
218 , simpleField "nicparams" [t| PartialNicParams |]
219 , optionalField $ simpleField "network" [t| Network |]
222 -- * Disk definitions
224 $(declareSADT "DiskMode"
225 [ ("DiskRdOnly", 'C.diskRdonly)
226 , ("DiskRdWr", 'C.diskRdwr)
228 $(makeJSONInstance ''DiskMode)
230 $(declareSADT "DiskType"
232 , ("LD_DRBD8", 'C.ldDrbd8)
233 , ("LD_FILE", 'C.ldFile)
234 , ("LD_BLOCKDEV", 'C.ldBlockdev)
235 , ("LD_RADOS", 'C.ldRbd)
236 , ("LD_EXT", 'C.ldExt)
238 $(makeJSONInstance ''DiskType)
240 -- | The persistent block driver type. Currently only one type is allowed.
241 $(declareSADT "BlockDriver"
242 [ ("BlockDrvManual", 'C.blockdevDriverManual)
244 $(makeJSONInstance ''BlockDriver)
246 -- | Constant for the dev_type key entry in the disk config.
250 -- | The disk configuration type. This includes the disk type itself,
251 -- for a more complete consistency. Note that since in the Python
252 -- code-base there's no authoritative place where we document the
253 -- logical id, this is probably a good reference point.
255 = LIDPlain String String -- ^ Volume group, logical volume
256 | LIDDrbd8 String String Int Int Int String
257 -- ^ NodeA, NodeB, Port, MinorA, MinorB, Secret
258 | LIDFile FileDriver String -- ^ Driver, path
259 | LIDBlockDev BlockDriver String -- ^ Driver, path (must be under /dev)
260 | LIDRados String String -- ^ Unused, path
261 | LIDExt String String -- ^ ExtProvider, unique name
264 -- | Mapping from a logical id to a disk type.
265 lidDiskType :: DiskLogicalId -> DiskType
266 lidDiskType (LIDPlain {}) = LD_LV
267 lidDiskType (LIDDrbd8 {}) = LD_DRBD8
268 lidDiskType (LIDFile {}) = LD_FILE
269 lidDiskType (LIDBlockDev {}) = LD_BLOCKDEV
270 lidDiskType (LIDRados {}) = LD_RADOS
271 lidDiskType (LIDExt {}) = LD_EXT
273 -- | Builds the extra disk_type field for a given logical id.
274 lidEncodeType :: DiskLogicalId -> [(String, JSValue)]
275 lidEncodeType v = [(devType, showJSON . lidDiskType $ v)]
277 -- | Custom encoder for DiskLogicalId (logical id only).
278 encodeDLId :: DiskLogicalId -> JSValue
279 encodeDLId (LIDPlain vg lv) = JSArray [showJSON vg, showJSON lv]
280 encodeDLId (LIDDrbd8 nodeA nodeB port minorA minorB key) =
281 JSArray [ showJSON nodeA, showJSON nodeB, showJSON port
282 , showJSON minorA, showJSON minorB, showJSON key ]
283 encodeDLId (LIDRados pool name) = JSArray [showJSON pool, showJSON name]
284 encodeDLId (LIDFile driver name) = JSArray [showJSON driver, showJSON name]
285 encodeDLId (LIDBlockDev driver name) = JSArray [showJSON driver, showJSON name]
286 encodeDLId (LIDExt extprovider name) =
287 JSArray [showJSON extprovider, showJSON name]
289 -- | Custom encoder for DiskLogicalId, composing both the logical id
290 -- and the extra disk_type field.
291 encodeFullDLId :: DiskLogicalId -> (JSValue, [(String, JSValue)])
292 encodeFullDLId v = (encodeDLId v, lidEncodeType v)
294 -- | Custom decoder for DiskLogicalId. This is manual for now, since
295 -- we don't have yet automation for separate-key style fields.
296 decodeDLId :: [(String, JSValue)] -> JSValue -> J.Result DiskLogicalId
297 decodeDLId obj lid = do
298 dtype <- fromObj obj devType
302 JSArray [nA, nB, p, mA, mB, k] -> do
309 return $ LIDDrbd8 nA' nB' p' mA' mB' k'
310 _ -> fail "Can't read logical_id for DRBD8 type"
313 JSArray [vg, lv] -> do
316 return $ LIDPlain vg' lv'
317 _ -> fail "Can't read logical_id for plain type"
320 JSArray [driver, path] -> do
321 driver' <- readJSON driver
322 path' <- readJSON path
323 return $ LIDFile driver' path'
324 _ -> fail "Can't read logical_id for file type"
327 JSArray [driver, path] -> do
328 driver' <- readJSON driver
329 path' <- readJSON path
330 return $ LIDBlockDev driver' path'
331 _ -> fail "Can't read logical_id for blockdev type"
334 JSArray [driver, path] -> do
335 driver' <- readJSON driver
336 path' <- readJSON path
337 return $ LIDRados driver' path'
338 _ -> fail "Can't read logical_id for rdb type"
341 JSArray [extprovider, name] -> do
342 extprovider' <- readJSON extprovider
343 name' <- readJSON name
344 return $ LIDExt extprovider' name'
345 _ -> fail "Can't read logical_id for extstorage type"
347 -- | Disk data structure.
349 -- This is declared manually as it's a recursive structure, and our TH
350 -- code currently can't build it.
352 { diskLogicalId :: DiskLogicalId
353 -- , diskPhysicalId :: String
354 , diskChildren :: [Disk]
355 , diskIvName :: String
357 , diskMode :: DiskMode
358 } deriving (Show, Eq)
360 $(buildObjectSerialisation "Disk"
361 [ customField 'decodeDLId 'encodeFullDLId ["dev_type"] $
362 simpleField "logical_id" [t| DiskLogicalId |]
363 -- , simpleField "physical_id" [t| String |]
364 , defaultField [| [] |] $ simpleField "children" [t| [Disk] |]
365 , defaultField [| "" |] $ simpleField "iv_name" [t| String |]
366 , simpleField "size" [t| Int |]
367 , defaultField [| DiskRdWr |] $ simpleField "mode" [t| DiskMode |]
370 -- * Instance definitions
372 $(declareSADT "AdminState"
373 [ ("AdminOffline", 'C.adminstOffline)
374 , ("AdminDown", 'C.adminstDown)
375 , ("AdminUp", 'C.adminstUp)
377 $(makeJSONInstance ''AdminState)
379 $(buildParam "Be" "bep"
380 [ simpleField "minmem" [t| Int |]
381 , simpleField "maxmem" [t| Int |]
382 , simpleField "vcpus" [t| Int |]
383 , simpleField "auto_balance" [t| Bool |]
386 $(buildObject "Instance" "inst" $
387 [ simpleField "name" [t| String |]
388 , simpleField "primary_node" [t| String |]
389 , simpleField "os" [t| String |]
390 , simpleField "hypervisor" [t| Hypervisor |]
391 , simpleField "hvparams" [t| HvParams |]
392 , simpleField "beparams" [t| PartialBeParams |]
393 , simpleField "osparams" [t| OsParams |]
394 , simpleField "admin_state" [t| AdminState |]
395 , simpleField "nics" [t| [PartialNic] |]
396 , simpleField "disks" [t| [Disk] |]
397 , simpleField "disk_template" [t| DiskTemplate |]
398 , optionalField $ simpleField "network_port" [t| Int |]
405 instance TimeStampObject Instance where
409 instance UuidObject Instance where
412 instance SerialNoObject Instance where
413 serialOf = instSerial
415 instance TagsObject Instance where
418 -- * IPolicy definitions
420 $(buildParam "ISpec" "ispec"
421 [ simpleField C.ispecMemSize [t| Int |]
422 , simpleField C.ispecDiskSize [t| Int |]
423 , simpleField C.ispecDiskCount [t| Int |]
424 , simpleField C.ispecCpuCount [t| Int |]
425 , simpleField C.ispecNicCount [t| Int |]
426 , simpleField C.ispecSpindleUse [t| Int |]
429 -- | Custom partial ipolicy. This is not built via buildParam since it
430 -- has a special 2-level inheritance mode.
431 $(buildObject "PartialIPolicy" "ipolicy"
432 [ renameField "MinSpecP" $ simpleField "min" [t| PartialISpecParams |]
433 , renameField "MaxSpecP" $ simpleField "max" [t| PartialISpecParams |]
434 , renameField "StdSpecP" $ simpleField "std" [t| PartialISpecParams |]
435 , optionalField . renameField "SpindleRatioP"
436 $ simpleField "spindle-ratio" [t| Double |]
437 , optionalField . renameField "VcpuRatioP"
438 $ simpleField "vcpu-ratio" [t| Double |]
439 , optionalField . renameField "DiskTemplatesP"
440 $ simpleField "disk-templates" [t| [DiskTemplate] |]
443 -- | Custom filled ipolicy. This is not built via buildParam since it
444 -- has a special 2-level inheritance mode.
445 $(buildObject "FilledIPolicy" "ipolicy"
446 [ renameField "MinSpec" $ simpleField "min" [t| FilledISpecParams |]
447 , renameField "MaxSpec" $ simpleField "max" [t| FilledISpecParams |]
448 , renameField "StdSpec" $ simpleField "std" [t| FilledISpecParams |]
449 , simpleField "spindle-ratio" [t| Double |]
450 , simpleField "vcpu-ratio" [t| Double |]
451 , simpleField "disk-templates" [t| [DiskTemplate] |]
454 -- | Custom filler for the ipolicy types.
455 fillIPolicy :: FilledIPolicy -> PartialIPolicy -> FilledIPolicy
456 fillIPolicy (FilledIPolicy { ipolicyMinSpec = fmin
457 , ipolicyMaxSpec = fmax
458 , ipolicyStdSpec = fstd
459 , ipolicySpindleRatio = fspindleRatio
460 , ipolicyVcpuRatio = fvcpuRatio
461 , ipolicyDiskTemplates = fdiskTemplates})
462 (PartialIPolicy { ipolicyMinSpecP = pmin
463 , ipolicyMaxSpecP = pmax
464 , ipolicyStdSpecP = pstd
465 , ipolicySpindleRatioP = pspindleRatio
466 , ipolicyVcpuRatioP = pvcpuRatio
467 , ipolicyDiskTemplatesP = pdiskTemplates}) =
468 FilledIPolicy { ipolicyMinSpec = fillISpecParams fmin pmin
469 , ipolicyMaxSpec = fillISpecParams fmax pmax
470 , ipolicyStdSpec = fillISpecParams fstd pstd
471 , ipolicySpindleRatio = fromMaybe fspindleRatio pspindleRatio
472 , ipolicyVcpuRatio = fromMaybe fvcpuRatio pvcpuRatio
473 , ipolicyDiskTemplates = fromMaybe fdiskTemplates
476 -- * Node definitions
478 $(buildParam "ND" "ndp"
479 [ simpleField "oob_program" [t| String |]
480 , simpleField "spindle_count" [t| Int |]
481 , simpleField "exclusive_storage" [t| Bool |]
484 $(buildObject "Node" "node" $
485 [ simpleField "name" [t| String |]
486 , simpleField "primary_ip" [t| String |]
487 , simpleField "secondary_ip" [t| String |]
488 , simpleField "master_candidate" [t| Bool |]
489 , simpleField "offline" [t| Bool |]
490 , simpleField "drained" [t| Bool |]
491 , simpleField "group" [t| String |]
492 , simpleField "master_capable" [t| Bool |]
493 , simpleField "vm_capable" [t| Bool |]
494 , simpleField "ndparams" [t| PartialNDParams |]
495 , simpleField "powered" [t| Bool |]
502 instance TimeStampObject Node where
506 instance UuidObject Node where
509 instance SerialNoObject Node where
510 serialOf = nodeSerial
512 instance TagsObject Node where
515 -- * NodeGroup definitions
517 -- | The disk parameters type.
518 type DiskParams = Container (Container JSValue)
520 -- | A mapping from network UUIDs to nic params of the networks.
521 type Networks = Container PartialNicParams
523 $(buildObject "NodeGroup" "group" $
524 [ simpleField "name" [t| String |]
525 , defaultField [| [] |] $ simpleField "members" [t| [String] |]
526 , simpleField "ndparams" [t| PartialNDParams |]
527 , simpleField "alloc_policy" [t| AllocPolicy |]
528 , simpleField "ipolicy" [t| PartialIPolicy |]
529 , simpleField "diskparams" [t| DiskParams |]
530 , simpleField "networks" [t| Networks |]
537 instance TimeStampObject NodeGroup where
541 instance UuidObject NodeGroup where
544 instance SerialNoObject NodeGroup where
545 serialOf = groupSerial
547 instance TagsObject NodeGroup where
551 $(declareIADT "IpFamily"
552 [ ("IpFamilyV4", 'C.ip4Family)
553 , ("IpFamilyV6", 'C.ip6Family)
555 $(makeJSONInstance ''IpFamily)
557 -- | Conversion from IP family to IP version. This is needed because
558 -- Python uses both, depending on context.
559 ipFamilyToVersion :: IpFamily -> Int
560 ipFamilyToVersion IpFamilyV4 = C.ip4Version
561 ipFamilyToVersion IpFamilyV6 = C.ip6Version
563 -- | Cluster HvParams (hvtype to hvparams mapping).
564 type ClusterHvParams = Container HvParams
566 -- | Cluster Os-HvParams (os to hvparams mapping).
567 type OsHvParams = Container ClusterHvParams
569 -- | Cluser BeParams.
570 type ClusterBeParams = Container FilledBeParams
572 -- | Cluster OsParams.
573 type ClusterOsParams = Container OsParams
575 -- | Cluster NicParams.
576 type ClusterNicParams = Container FilledNicParams
578 -- | Cluster UID Pool, list (low, high) UID ranges.
579 type UidPool = [(Int, Int)]
581 -- * Cluster definitions
582 $(buildObject "Cluster" "cluster" $
583 [ simpleField "rsahostkeypub" [t| String |]
584 , simpleField "highest_used_port" [t| Int |]
585 , simpleField "tcpudp_port_pool" [t| [Int] |]
586 , simpleField "mac_prefix" [t| String |]
587 , simpleField "volume_group_name" [t| String |]
588 , simpleField "reserved_lvs" [t| [String] |]
590 simpleField "drbd_usermode_helper" [t| String |]
591 , simpleField "master_node" [t| String |]
592 , simpleField "master_ip" [t| String |]
593 , simpleField "master_netdev" [t| String |]
594 , simpleField "master_netmask" [t| Int |]
595 , simpleField "use_external_mip_script" [t| Bool |]
596 , simpleField "cluster_name" [t| String |]
597 , simpleField "file_storage_dir" [t| String |]
598 , simpleField "shared_file_storage_dir" [t| String |]
599 , simpleField "enabled_hypervisors" [t| [Hypervisor] |]
600 , simpleField "hvparams" [t| ClusterHvParams |]
601 , simpleField "os_hvp" [t| OsHvParams |]
602 , simpleField "beparams" [t| ClusterBeParams |]
603 , simpleField "osparams" [t| ClusterOsParams |]
604 , simpleField "nicparams" [t| ClusterNicParams |]
605 , simpleField "ndparams" [t| FilledNDParams |]
606 , simpleField "diskparams" [t| DiskParams |]
607 , simpleField "candidate_pool_size" [t| Int |]
608 , simpleField "modify_etc_hosts" [t| Bool |]
609 , simpleField "modify_ssh_setup" [t| Bool |]
610 , simpleField "maintain_node_health" [t| Bool |]
611 , simpleField "uid_pool" [t| UidPool |]
612 , simpleField "default_iallocator" [t| String |]
613 , simpleField "hidden_os" [t| [String] |]
614 , simpleField "blacklisted_os" [t| [String] |]
615 , simpleField "primary_ip_family" [t| IpFamily |]
616 , simpleField "prealloc_wipe_disks" [t| Bool |]
617 , simpleField "ipolicy" [t| FilledIPolicy |]
624 instance TimeStampObject Cluster where
625 cTimeOf = clusterCtime
626 mTimeOf = clusterMtime
628 instance UuidObject Cluster where
631 instance SerialNoObject Cluster where
632 serialOf = clusterSerial
634 instance TagsObject Cluster where
637 -- * ConfigData definitions
639 $(buildObject "ConfigData" "config" $
640 -- timeStampFields ++
641 [ simpleField "version" [t| Int |]
642 , simpleField "cluster" [t| Cluster |]
643 , simpleField "nodes" [t| Container Node |]
644 , simpleField "nodegroups" [t| Container NodeGroup |]
645 , simpleField "instances" [t| Container Instance |]
646 , simpleField "networks" [t| Container Network |]
650 instance SerialNoObject ConfigData where
651 serialOf = configSerial