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 |]
198 instance SerialNoObject Network where
199 serialOf = networkSerial
201 instance TagsObject Network where
206 $(buildParam "Nic" "nicp"
207 [ simpleField "mode" [t| NICMode |]
208 , simpleField "link" [t| String |]
211 $(buildObject "PartialNic" "nic"
212 [ simpleField "mac" [t| String |]
213 , optionalField $ simpleField "ip" [t| String |]
214 , simpleField "nicparams" [t| PartialNicParams |]
215 , optionalField $ simpleField "network" [t| Network |]
218 -- * Disk definitions
220 $(declareSADT "DiskMode"
221 [ ("DiskRdOnly", 'C.diskRdonly)
222 , ("DiskRdWr", 'C.diskRdwr)
224 $(makeJSONInstance ''DiskMode)
226 $(declareSADT "DiskType"
228 , ("LD_DRBD8", 'C.ldDrbd8)
229 , ("LD_FILE", 'C.ldFile)
230 , ("LD_BLOCKDEV", 'C.ldBlockdev)
231 , ("LD_RADOS", 'C.ldRbd)
232 , ("LD_EXT", 'C.ldExt)
234 $(makeJSONInstance ''DiskType)
236 -- | The persistent block driver type. Currently only one type is allowed.
237 $(declareSADT "BlockDriver"
238 [ ("BlockDrvManual", 'C.blockdevDriverManual)
240 $(makeJSONInstance ''BlockDriver)
242 -- | Constant for the dev_type key entry in the disk config.
246 -- | The disk configuration type. This includes the disk type itself,
247 -- for a more complete consistency. Note that since in the Python
248 -- code-base there's no authoritative place where we document the
249 -- logical id, this is probably a good reference point.
251 = LIDPlain String String -- ^ Volume group, logical volume
252 | LIDDrbd8 String String Int Int Int String
253 -- ^ NodeA, NodeB, Port, MinorA, MinorB, Secret
254 | LIDFile FileDriver String -- ^ Driver, path
255 | LIDBlockDev BlockDriver String -- ^ Driver, path (must be under /dev)
256 | LIDRados String String -- ^ Unused, path
257 | LIDExt String String -- ^ ExtProvider, unique name
260 -- | Mapping from a logical id to a disk type.
261 lidDiskType :: DiskLogicalId -> DiskType
262 lidDiskType (LIDPlain {}) = LD_LV
263 lidDiskType (LIDDrbd8 {}) = LD_DRBD8
264 lidDiskType (LIDFile {}) = LD_FILE
265 lidDiskType (LIDBlockDev {}) = LD_BLOCKDEV
266 lidDiskType (LIDRados {}) = LD_RADOS
267 lidDiskType (LIDExt {}) = LD_EXT
269 -- | Builds the extra disk_type field for a given logical id.
270 lidEncodeType :: DiskLogicalId -> [(String, JSValue)]
271 lidEncodeType v = [(devType, showJSON . lidDiskType $ v)]
273 -- | Custom encoder for DiskLogicalId (logical id only).
274 encodeDLId :: DiskLogicalId -> JSValue
275 encodeDLId (LIDPlain vg lv) = JSArray [showJSON vg, showJSON lv]
276 encodeDLId (LIDDrbd8 nodeA nodeB port minorA minorB key) =
277 JSArray [ showJSON nodeA, showJSON nodeB, showJSON port
278 , showJSON minorA, showJSON minorB, showJSON key ]
279 encodeDLId (LIDRados pool name) = JSArray [showJSON pool, showJSON name]
280 encodeDLId (LIDFile driver name) = JSArray [showJSON driver, showJSON name]
281 encodeDLId (LIDBlockDev driver name) = JSArray [showJSON driver, showJSON name]
282 encodeDLId (LIDExt extprovider name) =
283 JSArray [showJSON extprovider, showJSON name]
285 -- | Custom encoder for DiskLogicalId, composing both the logical id
286 -- and the extra disk_type field.
287 encodeFullDLId :: DiskLogicalId -> (JSValue, [(String, JSValue)])
288 encodeFullDLId v = (encodeDLId v, lidEncodeType v)
290 -- | Custom decoder for DiskLogicalId. This is manual for now, since
291 -- we don't have yet automation for separate-key style fields.
292 decodeDLId :: [(String, JSValue)] -> JSValue -> J.Result DiskLogicalId
293 decodeDLId obj lid = do
294 dtype <- fromObj obj devType
298 JSArray [nA, nB, p, mA, mB, k] -> do
305 return $ LIDDrbd8 nA' nB' p' mA' mB' k'
306 _ -> fail "Can't read logical_id for DRBD8 type"
309 JSArray [vg, lv] -> do
312 return $ LIDPlain vg' lv'
313 _ -> fail "Can't read logical_id for plain type"
316 JSArray [driver, path] -> do
317 driver' <- readJSON driver
318 path' <- readJSON path
319 return $ LIDFile driver' path'
320 _ -> fail "Can't read logical_id for file type"
323 JSArray [driver, path] -> do
324 driver' <- readJSON driver
325 path' <- readJSON path
326 return $ LIDBlockDev driver' path'
327 _ -> fail "Can't read logical_id for blockdev type"
330 JSArray [driver, path] -> do
331 driver' <- readJSON driver
332 path' <- readJSON path
333 return $ LIDRados driver' path'
334 _ -> fail "Can't read logical_id for rdb type"
337 JSArray [extprovider, name] -> do
338 extprovider' <- readJSON extprovider
339 name' <- readJSON name
340 return $ LIDExt extprovider' name'
341 _ -> fail "Can't read logical_id for extstorage type"
343 -- | Disk data structure.
345 -- This is declared manually as it's a recursive structure, and our TH
346 -- code currently can't build it.
348 { diskLogicalId :: DiskLogicalId
349 -- , diskPhysicalId :: String
350 , diskChildren :: [Disk]
351 , diskIvName :: String
353 , diskMode :: DiskMode
354 } deriving (Show, Eq)
356 $(buildObjectSerialisation "Disk"
357 [ customField 'decodeDLId 'encodeFullDLId ["dev_type"] $
358 simpleField "logical_id" [t| DiskLogicalId |]
359 -- , simpleField "physical_id" [t| String |]
360 , defaultField [| [] |] $ simpleField "children" [t| [Disk] |]
361 , defaultField [| "" |] $ simpleField "iv_name" [t| String |]
362 , simpleField "size" [t| Int |]
363 , defaultField [| DiskRdWr |] $ simpleField "mode" [t| DiskMode |]
366 -- * Instance definitions
368 $(declareSADT "AdminState"
369 [ ("AdminOffline", 'C.adminstOffline)
370 , ("AdminDown", 'C.adminstDown)
371 , ("AdminUp", 'C.adminstUp)
373 $(makeJSONInstance ''AdminState)
375 $(buildParam "Be" "bep"
376 [ simpleField "minmem" [t| Int |]
377 , simpleField "maxmem" [t| Int |]
378 , simpleField "vcpus" [t| Int |]
379 , simpleField "auto_balance" [t| Bool |]
382 $(buildObject "Instance" "inst" $
383 [ simpleField "name" [t| String |]
384 , simpleField "primary_node" [t| String |]
385 , simpleField "os" [t| String |]
386 , simpleField "hypervisor" [t| Hypervisor |]
387 , simpleField "hvparams" [t| HvParams |]
388 , simpleField "beparams" [t| PartialBeParams |]
389 , simpleField "osparams" [t| OsParams |]
390 , simpleField "admin_state" [t| AdminState |]
391 , simpleField "nics" [t| [PartialNic] |]
392 , simpleField "disks" [t| [Disk] |]
393 , simpleField "disk_template" [t| DiskTemplate |]
394 , optionalField $ simpleField "network_port" [t| Int |]
401 instance TimeStampObject Instance where
405 instance UuidObject Instance where
408 instance SerialNoObject Instance where
409 serialOf = instSerial
411 instance TagsObject Instance where
414 -- * IPolicy definitions
416 $(buildParam "ISpec" "ispec"
417 [ simpleField C.ispecMemSize [t| Int |]
418 , simpleField C.ispecDiskSize [t| Int |]
419 , simpleField C.ispecDiskCount [t| Int |]
420 , simpleField C.ispecCpuCount [t| Int |]
421 , simpleField C.ispecNicCount [t| Int |]
422 , simpleField C.ispecSpindleUse [t| Int |]
425 -- | Custom partial ipolicy. This is not built via buildParam since it
426 -- has a special 2-level inheritance mode.
427 $(buildObject "PartialIPolicy" "ipolicy"
428 [ renameField "MinSpecP" $ simpleField "min" [t| PartialISpecParams |]
429 , renameField "MaxSpecP" $ simpleField "max" [t| PartialISpecParams |]
430 , renameField "StdSpecP" $ simpleField "std" [t| PartialISpecParams |]
431 , optionalField . renameField "SpindleRatioP"
432 $ simpleField "spindle-ratio" [t| Double |]
433 , optionalField . renameField "VcpuRatioP"
434 $ simpleField "vcpu-ratio" [t| Double |]
435 , optionalField . renameField "DiskTemplatesP"
436 $ simpleField "disk-templates" [t| [DiskTemplate] |]
439 -- | Custom filled ipolicy. This is not built via buildParam since it
440 -- has a special 2-level inheritance mode.
441 $(buildObject "FilledIPolicy" "ipolicy"
442 [ renameField "MinSpec" $ simpleField "min" [t| FilledISpecParams |]
443 , renameField "MaxSpec" $ simpleField "max" [t| FilledISpecParams |]
444 , renameField "StdSpec" $ simpleField "std" [t| FilledISpecParams |]
445 , simpleField "spindle-ratio" [t| Double |]
446 , simpleField "vcpu-ratio" [t| Double |]
447 , simpleField "disk-templates" [t| [DiskTemplate] |]
450 -- | Custom filler for the ipolicy types.
451 fillIPolicy :: FilledIPolicy -> PartialIPolicy -> FilledIPolicy
452 fillIPolicy (FilledIPolicy { ipolicyMinSpec = fmin
453 , ipolicyMaxSpec = fmax
454 , ipolicyStdSpec = fstd
455 , ipolicySpindleRatio = fspindleRatio
456 , ipolicyVcpuRatio = fvcpuRatio
457 , ipolicyDiskTemplates = fdiskTemplates})
458 (PartialIPolicy { ipolicyMinSpecP = pmin
459 , ipolicyMaxSpecP = pmax
460 , ipolicyStdSpecP = pstd
461 , ipolicySpindleRatioP = pspindleRatio
462 , ipolicyVcpuRatioP = pvcpuRatio
463 , ipolicyDiskTemplatesP = pdiskTemplates}) =
464 FilledIPolicy { ipolicyMinSpec = fillISpecParams fmin pmin
465 , ipolicyMaxSpec = fillISpecParams fmax pmax
466 , ipolicyStdSpec = fillISpecParams fstd pstd
467 , ipolicySpindleRatio = fromMaybe fspindleRatio pspindleRatio
468 , ipolicyVcpuRatio = fromMaybe fvcpuRatio pvcpuRatio
469 , ipolicyDiskTemplates = fromMaybe fdiskTemplates
472 -- * Node definitions
474 $(buildParam "ND" "ndp"
475 [ simpleField "oob_program" [t| String |]
476 , simpleField "spindle_count" [t| Int |]
477 , simpleField "exclusive_storage" [t| Bool |]
480 $(buildObject "Node" "node" $
481 [ simpleField "name" [t| String |]
482 , simpleField "primary_ip" [t| String |]
483 , simpleField "secondary_ip" [t| String |]
484 , simpleField "master_candidate" [t| Bool |]
485 , simpleField "offline" [t| Bool |]
486 , simpleField "drained" [t| Bool |]
487 , simpleField "group" [t| String |]
488 , simpleField "master_capable" [t| Bool |]
489 , simpleField "vm_capable" [t| Bool |]
490 , simpleField "ndparams" [t| PartialNDParams |]
491 , simpleField "powered" [t| Bool |]
498 instance TimeStampObject Node where
502 instance UuidObject Node where
505 instance SerialNoObject Node where
506 serialOf = nodeSerial
508 instance TagsObject Node where
511 -- * NodeGroup definitions
513 -- | The disk parameters type.
514 type DiskParams = Container (Container JSValue)
516 -- | A mapping from network UUIDs to nic params of the networks.
517 type Networks = Container PartialNic
519 $(buildObject "NodeGroup" "group" $
520 [ simpleField "name" [t| String |]
521 , defaultField [| [] |] $ simpleField "members" [t| [String] |]
522 , simpleField "ndparams" [t| PartialNDParams |]
523 , simpleField "alloc_policy" [t| AllocPolicy |]
524 , simpleField "ipolicy" [t| PartialIPolicy |]
525 , simpleField "diskparams" [t| DiskParams |]
526 , simpleField "networks" [t| Networks |]
533 instance TimeStampObject NodeGroup where
537 instance UuidObject NodeGroup where
540 instance SerialNoObject NodeGroup where
541 serialOf = groupSerial
543 instance TagsObject NodeGroup where
547 $(declareIADT "IpFamily"
548 [ ("IpFamilyV4", 'C.ip4Family)
549 , ("IpFamilyV6", 'C.ip6Family)
551 $(makeJSONInstance ''IpFamily)
553 -- | Conversion from IP family to IP version. This is needed because
554 -- Python uses both, depending on context.
555 ipFamilyToVersion :: IpFamily -> Int
556 ipFamilyToVersion IpFamilyV4 = C.ip4Version
557 ipFamilyToVersion IpFamilyV6 = C.ip6Version
559 -- | Cluster HvParams (hvtype to hvparams mapping).
560 type ClusterHvParams = Container HvParams
562 -- | Cluster Os-HvParams (os to hvparams mapping).
563 type OsHvParams = Container ClusterHvParams
565 -- | Cluser BeParams.
566 type ClusterBeParams = Container FilledBeParams
568 -- | Cluster OsParams.
569 type ClusterOsParams = Container OsParams
571 -- | Cluster NicParams.
572 type ClusterNicParams = Container FilledNicParams
574 -- | Cluster UID Pool, list (low, high) UID ranges.
575 type UidPool = [(Int, Int)]
577 -- * Cluster definitions
578 $(buildObject "Cluster" "cluster" $
579 [ simpleField "rsahostkeypub" [t| String |]
580 , simpleField "highest_used_port" [t| Int |]
581 , simpleField "tcpudp_port_pool" [t| [Int] |]
582 , simpleField "mac_prefix" [t| String |]
583 , simpleField "volume_group_name" [t| String |]
584 , simpleField "reserved_lvs" [t| [String] |]
586 simpleField "drbd_usermode_helper" [t| String |]
587 , simpleField "master_node" [t| String |]
588 , simpleField "master_ip" [t| String |]
589 , simpleField "master_netdev" [t| String |]
590 , simpleField "master_netmask" [t| Int |]
591 , simpleField "use_external_mip_script" [t| Bool |]
592 , simpleField "cluster_name" [t| String |]
593 , simpleField "file_storage_dir" [t| String |]
594 , simpleField "shared_file_storage_dir" [t| String |]
595 , simpleField "enabled_hypervisors" [t| [Hypervisor] |]
596 , simpleField "hvparams" [t| ClusterHvParams |]
597 , simpleField "os_hvp" [t| OsHvParams |]
598 , simpleField "beparams" [t| ClusterBeParams |]
599 , simpleField "osparams" [t| ClusterOsParams |]
600 , simpleField "nicparams" [t| ClusterNicParams |]
601 , simpleField "ndparams" [t| FilledNDParams |]
602 , simpleField "diskparams" [t| DiskParams |]
603 , simpleField "candidate_pool_size" [t| Int |]
604 , simpleField "modify_etc_hosts" [t| Bool |]
605 , simpleField "modify_ssh_setup" [t| Bool |]
606 , simpleField "maintain_node_health" [t| Bool |]
607 , simpleField "uid_pool" [t| UidPool |]
608 , simpleField "default_iallocator" [t| String |]
609 , simpleField "hidden_os" [t| [String] |]
610 , simpleField "blacklisted_os" [t| [String] |]
611 , simpleField "primary_ip_family" [t| IpFamily |]
612 , simpleField "prealloc_wipe_disks" [t| Bool |]
613 , simpleField "ipolicy" [t| FilledIPolicy |]
620 instance TimeStampObject Cluster where
621 cTimeOf = clusterCtime
622 mTimeOf = clusterMtime
624 instance UuidObject Cluster where
627 instance SerialNoObject Cluster where
628 serialOf = clusterSerial
630 instance TagsObject Cluster where
633 -- * ConfigData definitions
635 $(buildObject "ConfigData" "config" $
636 -- timeStampFields ++
637 [ simpleField "version" [t| Int |]
638 , simpleField "cluster" [t| Cluster |]
639 , simpleField "nodes" [t| Container Node |]
640 , simpleField "nodegroups" [t| Container NodeGroup |]
641 , simpleField "instances" [t| Container Instance |]
645 instance SerialNoObject ConfigData where
646 serialOf = configSerial