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
37 , PartialNicParams(..)
67 , FilledISpecParams(..)
68 , PartialISpecParams(..)
90 , DictObject(..) -- 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 (makeObj, 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"
171 $(declareSADT "NICMode"
172 [ ("NMBridged", 'C.nicModeBridged)
173 , ("NMRouted", 'C.nicModeRouted)
175 $(makeJSONInstance ''NICMode)
177 $(buildParam "Nic" "nicp"
178 [ simpleField "mode" [t| NICMode |]
179 , simpleField "link" [t| String |]
182 $(buildObject "PartialNic" "nic"
183 [ simpleField "mac" [t| String |]
184 , optionalField $ simpleField "ip" [t| String |]
185 , simpleField "nicparams" [t| PartialNicParams |]
188 -- * Disk definitions
190 $(declareSADT "DiskMode"
191 [ ("DiskRdOnly", 'C.diskRdonly)
192 , ("DiskRdWr", 'C.diskRdwr)
194 $(makeJSONInstance ''DiskMode)
196 $(declareSADT "DiskType"
198 , ("LD_DRBD8", 'C.ldDrbd8)
199 , ("LD_FILE", 'C.ldFile)
200 , ("LD_BLOCKDEV", 'C.ldBlockdev)
201 , ("LD_RADOS", 'C.ldRbd)
203 $(makeJSONInstance ''DiskType)
205 -- | The file driver type.
206 $(declareSADT "FileDriver"
207 [ ("FileLoop", 'C.fdLoop)
208 , ("FileBlktap", 'C.fdBlktap)
210 $(makeJSONInstance ''FileDriver)
212 -- | The persistent block driver type. Currently only one type is allowed.
213 $(declareSADT "BlockDriver"
214 [ ("BlockDrvManual", 'C.blockdevDriverManual)
216 $(makeJSONInstance ''BlockDriver)
218 -- | Constant for the dev_type key entry in the disk config.
222 -- | The disk configuration type. This includes the disk type itself,
223 -- for a more complete consistency. Note that since in the Python
224 -- code-base there's no authoritative place where we document the
225 -- logical id, this is probably a good reference point.
227 = LIDPlain String String -- ^ Volume group, logical volume
228 | LIDDrbd8 String String Int Int Int String
229 -- ^ NodeA, NodeB, Port, MinorA, MinorB, Secret
230 | LIDFile FileDriver String -- ^ Driver, path
231 | LIDBlockDev BlockDriver String -- ^ Driver, path (must be under /dev)
232 | LIDRados String String -- ^ Unused, path
233 deriving (Read, Show, Eq)
235 -- | Mapping from a logical id to a disk type.
236 lidDiskType :: DiskLogicalId -> DiskType
237 lidDiskType (LIDPlain {}) = LD_LV
238 lidDiskType (LIDDrbd8 {}) = LD_DRBD8
239 lidDiskType (LIDFile {}) = LD_FILE
240 lidDiskType (LIDBlockDev {}) = LD_BLOCKDEV
241 lidDiskType (LIDRados {}) = LD_RADOS
243 -- | Builds the extra disk_type field for a given logical id.
244 lidEncodeType :: DiskLogicalId -> [(String, JSValue)]
245 lidEncodeType v = [(devType, showJSON . lidDiskType $ v)]
247 -- | Custom encoder for DiskLogicalId (logical id only).
248 encodeDLId :: DiskLogicalId -> JSValue
249 encodeDLId (LIDPlain vg lv) = JSArray [showJSON vg, showJSON lv]
250 encodeDLId (LIDDrbd8 nodeA nodeB port minorA minorB key) =
251 JSArray [ showJSON nodeA, showJSON nodeB, showJSON port
252 , showJSON minorA, showJSON minorB, showJSON key ]
253 encodeDLId (LIDRados pool name) = JSArray [showJSON pool, showJSON name]
254 encodeDLId (LIDFile driver name) = JSArray [showJSON driver, showJSON name]
255 encodeDLId (LIDBlockDev driver name) = JSArray [showJSON driver, showJSON name]
257 -- | Custom encoder for DiskLogicalId, composing both the logical id
258 -- and the extra disk_type field.
259 encodeFullDLId :: DiskLogicalId -> (JSValue, [(String, JSValue)])
260 encodeFullDLId v = (encodeDLId v, lidEncodeType v)
262 -- | Custom decoder for DiskLogicalId. This is manual for now, since
263 -- we don't have yet automation for separate-key style fields.
264 decodeDLId :: [(String, JSValue)] -> JSValue -> J.Result DiskLogicalId
265 decodeDLId obj lid = do
266 dtype <- fromObj obj devType
270 JSArray [nA, nB, p, mA, mB, k] -> do
277 return $ LIDDrbd8 nA' nB' p' mA' mB' k'
278 _ -> fail $ "Can't read logical_id for DRBD8 type"
281 JSArray [vg, lv] -> do
284 return $ LIDPlain vg' lv'
285 _ -> fail $ "Can't read logical_id for plain type"
288 JSArray [driver, path] -> do
289 driver' <- readJSON driver
290 path' <- readJSON path
291 return $ LIDFile driver' path'
292 _ -> fail $ "Can't read logical_id for file type"
295 JSArray [driver, path] -> do
296 driver' <- readJSON driver
297 path' <- readJSON path
298 return $ LIDBlockDev driver' path'
299 _ -> fail $ "Can't read logical_id for blockdev type"
302 JSArray [driver, path] -> do
303 driver' <- readJSON driver
304 path' <- readJSON path
305 return $ LIDRados driver' path'
306 _ -> fail $ "Can't read logical_id for rdb type"
308 -- | Disk data structure.
310 -- This is declared manually as it's a recursive structure, and our TH
311 -- code currently can't build it.
313 { diskLogicalId :: DiskLogicalId
314 -- , diskPhysicalId :: String
315 , diskChildren :: [Disk]
316 , diskIvName :: String
318 , diskMode :: DiskMode
319 } deriving (Read, Show, Eq)
321 $(buildObjectSerialisation "Disk"
322 [ customField 'decodeDLId 'encodeFullDLId $
323 simpleField "logical_id" [t| DiskLogicalId |]
324 -- , simpleField "physical_id" [t| String |]
325 , defaultField [| [] |] $ simpleField "children" [t| [Disk] |]
326 , defaultField [| "" |] $ simpleField "iv_name" [t| String |]
327 , simpleField "size" [t| Int |]
328 , defaultField [| DiskRdWr |] $ simpleField "mode" [t| DiskMode |]
331 -- * Hypervisor definitions
333 -- | This may be due to change when we add hypervisor parameters.
334 $(declareSADT "Hypervisor"
335 [ ( "Kvm", 'C.htKvm )
336 , ( "XenPvm", 'C.htXenPvm )
337 , ( "Chroot", 'C.htChroot )
338 , ( "XenHvm", 'C.htXenHvm )
339 , ( "Lxc", 'C.htLxc )
340 , ( "Fake", 'C.htFake )
342 $(makeJSONInstance ''Hypervisor)
344 -- * Instance definitions
346 -- | Instance disk template type. **Copied from HTools/Types.hs**
347 $(declareSADT "DiskTemplate"
348 [ ("DTDiskless", 'C.dtDiskless)
349 , ("DTFile", 'C.dtFile)
350 , ("DTSharedFile", 'C.dtSharedFile)
351 , ("DTPlain", 'C.dtPlain)
352 , ("DTBlock", 'C.dtBlock)
353 , ("DTDrbd8", 'C.dtDrbd8)
354 , ("DTRados", 'C.dtRbd)
356 $(makeJSONInstance ''DiskTemplate)
358 $(declareSADT "AdminState"
359 [ ("AdminOffline", 'C.adminstOffline)
360 , ("AdminDown", 'C.adminstDown)
361 , ("AdminUp", 'C.adminstUp)
363 $(makeJSONInstance ''AdminState)
365 $(buildParam "Be" "bep" $
366 [ simpleField "minmem" [t| Int |]
367 , simpleField "maxmem" [t| Int |]
368 , simpleField "vcpus" [t| Int |]
369 , simpleField "auto_balance" [t| Bool |]
372 $(buildObject "Instance" "inst" $
373 [ simpleField "name" [t| String |]
374 , simpleField "primary_node" [t| String |]
375 , simpleField "os" [t| String |]
376 , simpleField "hypervisor" [t| Hypervisor |]
377 , simpleField "hvparams" [t| HvParams |]
378 , simpleField "beparams" [t| PartialBeParams |]
379 , simpleField "osparams" [t| OsParams |]
380 , simpleField "admin_state" [t| AdminState |]
381 , simpleField "nics" [t| [PartialNic] |]
382 , simpleField "disks" [t| [Disk] |]
383 , simpleField "disk_template" [t| DiskTemplate |]
384 , optionalField $ simpleField "network_port" [t| Int |]
391 instance TimeStampObject Instance where
395 instance UuidObject Instance where
398 instance SerialNoObject Instance where
399 serialOf = instSerial
401 instance TagsObject Instance where
404 -- * IPolicy definitions
406 $(buildParam "ISpec" "ispec" $
407 [ simpleField C.ispecMemSize [t| Int |]
408 , simpleField C.ispecDiskSize [t| Int |]
409 , simpleField C.ispecDiskCount [t| Int |]
410 , simpleField C.ispecCpuCount [t| Int |]
411 , simpleField C.ispecSpindleUse [t| Int |]
414 -- | Custom partial ipolicy. This is not built via buildParam since it
415 -- has a special 2-level inheritance mode.
416 $(buildObject "PartialIPolicy" "ipolicy" $
417 [ renameField "MinSpecP" $ simpleField "min" [t| PartialISpecParams |]
418 , renameField "MaxSpecP" $ simpleField "max" [t| PartialISpecParams |]
419 , renameField "StdSpecP" $ simpleField "std" [t| PartialISpecParams |]
420 , optionalField . renameField "SpindleRatioP"
421 $ simpleField "spindle-ratio" [t| Double |]
422 , optionalField . renameField "VcpuRatioP"
423 $ simpleField "vcpu-ratio" [t| Double |]
424 , optionalField . renameField "DiskTemplatesP"
425 $ simpleField "disk-templates" [t| [DiskTemplate] |]
428 -- | Custom filled ipolicy. This is not built via buildParam since it
429 -- has a special 2-level inheritance mode.
430 $(buildObject "FilledIPolicy" "ipolicy" $
431 [ renameField "MinSpec" $ simpleField "min" [t| FilledISpecParams |]
432 , renameField "MaxSpec" $ simpleField "max" [t| FilledISpecParams |]
433 , renameField "StdSpec" $ simpleField "std" [t| FilledISpecParams |]
434 , simpleField "spindle-ratio" [t| Double |]
435 , simpleField "vcpu-ratio" [t| Double |]
436 , simpleField "disk-templates" [t| [DiskTemplate] |]
439 -- | Custom filler for the ipolicy types.
440 fillIPolicy :: FilledIPolicy -> PartialIPolicy -> FilledIPolicy
441 fillIPolicy (FilledIPolicy { ipolicyMinSpec = fmin
442 , ipolicyMaxSpec = fmax
443 , ipolicyStdSpec = fstd
444 , ipolicySpindleRatio = fspindleRatio
445 , ipolicyVcpuRatio = fvcpuRatio
446 , ipolicyDiskTemplates = fdiskTemplates})
447 (PartialIPolicy { ipolicyMinSpecP = pmin
448 , ipolicyMaxSpecP = pmax
449 , ipolicyStdSpecP = pstd
450 , ipolicySpindleRatioP = pspindleRatio
451 , ipolicyVcpuRatioP = pvcpuRatio
452 , ipolicyDiskTemplatesP = pdiskTemplates}) =
453 FilledIPolicy { ipolicyMinSpec = fillISpecParams fmin pmin
454 , ipolicyMaxSpec = fillISpecParams fmax pmax
455 , ipolicyStdSpec = fillISpecParams fstd pstd
456 , ipolicySpindleRatio = fromMaybe fspindleRatio pspindleRatio
457 , ipolicyVcpuRatio = fromMaybe fvcpuRatio pvcpuRatio
458 , ipolicyDiskTemplates = fromMaybe fdiskTemplates
461 -- * Node definitions
463 $(buildParam "ND" "ndp" $
464 [ simpleField "oob_program" [t| String |]
465 , simpleField "spindle_count" [t| Int |]
468 $(buildObject "Node" "node" $
469 [ simpleField "name" [t| String |]
470 , simpleField "primary_ip" [t| String |]
471 , simpleField "secondary_ip" [t| String |]
472 , simpleField "master_candidate" [t| Bool |]
473 , simpleField "offline" [t| Bool |]
474 , simpleField "drained" [t| Bool |]
475 , simpleField "group" [t| String |]
476 , simpleField "master_capable" [t| Bool |]
477 , simpleField "vm_capable" [t| Bool |]
478 , simpleField "ndparams" [t| PartialNDParams |]
479 , simpleField "powered" [t| Bool |]
486 instance TimeStampObject Node where
490 instance UuidObject Node where
493 instance SerialNoObject Node where
494 serialOf = nodeSerial
496 instance TagsObject Node where
499 -- * NodeGroup definitions
501 -- | The Group allocation policy type.
503 -- Note that the order of constructors is important as the automatic
504 -- Ord instance will order them in the order they are defined, so when
505 -- changing this data type be careful about the interaction with the
506 -- desired sorting order.
508 -- FIXME: COPIED from Types.hs; we need to eliminate this duplication later
509 $(declareSADT "AllocPolicy"
510 [ ("AllocPreferred", 'C.allocPolicyPreferred)
511 , ("AllocLastResort", 'C.allocPolicyLastResort)
512 , ("AllocUnallocable", 'C.allocPolicyUnallocable)
514 $(makeJSONInstance ''AllocPolicy)
516 -- | The disk parameters type.
517 type DiskParams = Container (Container JSValue)
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 |]
532 instance TimeStampObject NodeGroup where
536 instance UuidObject NodeGroup where
539 instance SerialNoObject NodeGroup where
540 serialOf = groupSerial
542 instance TagsObject NodeGroup where
546 $(declareIADT "IpFamily"
547 [ ("IpFamilyV4", 'C.ip4Family)
548 , ("IpFamilyV6", 'C.ip6Family)
550 $(makeJSONInstance ''IpFamily)
552 -- | Conversion from IP family to IP version. This is needed because
553 -- Python uses both, depending on context.
554 ipFamilyToVersion :: IpFamily -> Int
555 ipFamilyToVersion IpFamilyV4 = C.ip4Version
556 ipFamilyToVersion IpFamilyV6 = C.ip6Version
558 -- | Cluster HvParams (hvtype to hvparams mapping).
559 type ClusterHvParams = Container HvParams
561 -- | Cluster Os-HvParams (os to hvparams mapping).
562 type OsHvParams = Container ClusterHvParams
564 -- | Cluser BeParams.
565 type ClusterBeParams = Container FilledBeParams
567 -- | Cluster OsParams.
568 type ClusterOsParams = Container OsParams
570 -- | Cluster NicParams.
571 type ClusterNicParams = Container FilledNicParams
573 -- | Cluster UID Pool, list (low, high) UID ranges.
574 type UidPool = [(Int, Int)]
576 -- * Cluster definitions
577 $(buildObject "Cluster" "cluster" $
578 [ simpleField "rsahostkeypub" [t| String |]
579 , simpleField "highest_used_port" [t| Int |]
580 , simpleField "tcpudp_port_pool" [t| [Int] |]
581 , simpleField "mac_prefix" [t| String |]
582 , simpleField "volume_group_name" [t| String |]
583 , simpleField "reserved_lvs" [t| [String] |]
585 simpleField "drbd_usermode_helper" [t| String |]
586 , simpleField "master_node" [t| String |]
587 , simpleField "master_ip" [t| String |]
588 , simpleField "master_netdev" [t| String |]
589 , simpleField "master_netmask" [t| Int |]
590 , simpleField "use_external_mip_script" [t| Bool |]
591 , simpleField "cluster_name" [t| String |]
592 , simpleField "file_storage_dir" [t| String |]
593 , simpleField "shared_file_storage_dir" [t| String |]
594 , simpleField "enabled_hypervisors" [t| [String] |]
595 , simpleField "hvparams" [t| ClusterHvParams |]
596 , simpleField "os_hvp" [t| OsHvParams |]
597 , simpleField "beparams" [t| ClusterBeParams |]
598 , simpleField "osparams" [t| ClusterOsParams |]
599 , simpleField "nicparams" [t| ClusterNicParams |]
600 , simpleField "ndparams" [t| FilledNDParams |]
601 , simpleField "diskparams" [t| DiskParams |]
602 , simpleField "candidate_pool_size" [t| Int |]
603 , simpleField "modify_etc_hosts" [t| Bool |]
604 , simpleField "modify_ssh_setup" [t| Bool |]
605 , simpleField "maintain_node_health" [t| Bool |]
606 , simpleField "uid_pool" [t| UidPool |]
607 , simpleField "default_iallocator" [t| String |]
608 , simpleField "hidden_os" [t| [String] |]
609 , simpleField "blacklisted_os" [t| [String] |]
610 , simpleField "primary_ip_family" [t| IpFamily |]
611 , simpleField "prealloc_wipe_disks" [t| Bool |]
612 , simpleField "ipolicy" [t| FilledIPolicy |]
619 instance TimeStampObject Cluster where
620 cTimeOf = clusterCtime
621 mTimeOf = clusterMtime
623 instance UuidObject Cluster where
626 instance SerialNoObject Cluster where
627 serialOf = clusterSerial
629 instance TagsObject Cluster where
632 -- * ConfigData definitions
634 $(buildObject "ConfigData" "config" $
635 -- timeStampFields ++
636 [ simpleField "version" [t| Int |]
637 , simpleField "cluster" [t| Cluster |]
638 , simpleField "nodes" [t| Container Node |]
639 , simpleField "nodegroups" [t| Container NodeGroup |]
640 , simpleField "instances" [t| Container Instance |]
644 instance SerialNoObject ConfigData where
645 serialOf = configSerial