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(..)
65 , FilledISpecParams(..)
66 , PartialISpecParams(..)
88 , DictObject(..) -- re-exported from THH
91 import Data.List (foldl')
93 import qualified Data.Map as Map
94 import qualified Data.Set as Set
95 import Text.JSON (makeObj, showJSON, readJSON, JSON, JSValue(..))
96 import qualified Text.JSON as J
98 import qualified Ganeti.Constants as C
99 import Ganeti.HTools.JSON
103 -- * Generic definitions
105 -- | Fills one map with keys from the other map, if not already
106 -- existing. Mirrors objects.py:FillDict.
107 fillDict :: (Ord k) => Map.Map k v -> Map.Map k v -> [k] -> Map.Map k v
108 fillDict defaults custom skip_keys =
109 let updated = Map.union custom defaults
110 in foldl' (flip Map.delete) updated skip_keys
112 -- | The VTYPES, a mini-type system in Python.
113 $(declareSADT "VType"
114 [ ("VTypeString", 'C.vtypeString)
115 , ("VTypeMaybeString", 'C.vtypeMaybeString)
116 , ("VTypeBool", 'C.vtypeBool)
117 , ("VTypeSize", 'C.vtypeSize)
118 , ("VTypeInt", 'C.vtypeInt)
120 $(makeJSONInstance ''VType)
122 -- | The hypervisor parameter type. This is currently a simple map,
123 -- without type checking on key/value pairs.
124 type HvParams = Container JSValue
126 -- | The OS parameters type. This is, and will remain, a string
127 -- container, since the keys are dynamically declared by the OSes, and
128 -- the values are always strings.
129 type OsParams = Container String
131 -- | Class of objects that have timestamps.
132 class TimeStampObject a where
133 cTimeOf :: a -> Double
134 mTimeOf :: a -> Double
136 -- | Class of objects that have an UUID.
137 class UuidObject a where
138 uuidOf :: a -> String
140 -- | Class of object that have a serial number.
141 class SerialNoObject a where
144 -- | Class of objects that have tags.
145 class TagsObject a where
146 tagsOf :: a -> Set.Set String
148 -- * Node role object
150 $(declareSADT "NodeRole"
151 [ ("NROffline", 'C.nrOffline)
152 , ("NRDrained", 'C.nrDrained)
153 , ("NRRegular", 'C.nrRegular)
154 , ("NRCandidate", 'C.nrMcandidate)
155 , ("NRMaster", 'C.nrMaster)
157 $(makeJSONInstance ''NodeRole)
159 -- | The description of the node role.
160 roleDescription :: NodeRole -> String
161 roleDescription NROffline = "offline"
162 roleDescription NRDrained = "drained"
163 roleDescription NRRegular = "regular"
164 roleDescription NRCandidate = "master candidate"
165 roleDescription NRMaster = "master"
169 $(declareSADT "NICMode"
170 [ ("NMBridged", 'C.nicModeBridged)
171 , ("NMRouted", 'C.nicModeRouted)
173 $(makeJSONInstance ''NICMode)
175 $(buildParam "Nic" "nicp"
176 [ simpleField "mode" [t| NICMode |]
177 , simpleField "link" [t| String |]
180 $(buildObject "PartialNic" "nic"
181 [ simpleField "mac" [t| String |]
182 , optionalField $ simpleField "ip" [t| String |]
183 , simpleField "nicparams" [t| PartialNicParams |]
186 -- * Disk definitions
188 $(declareSADT "DiskMode"
189 [ ("DiskRdOnly", 'C.diskRdonly)
190 , ("DiskRdWr", 'C.diskRdwr)
192 $(makeJSONInstance ''DiskMode)
194 $(declareSADT "DiskType"
196 , ("LD_DRBD8", 'C.ldDrbd8)
197 , ("LD_FILE", 'C.ldFile)
198 , ("LD_BLOCKDEV", 'C.ldBlockdev)
199 , ("LD_RADOS", 'C.ldRbd)
201 $(makeJSONInstance ''DiskType)
203 -- | The file driver type.
204 $(declareSADT "FileDriver"
205 [ ("FileLoop", 'C.fdLoop)
206 , ("FileBlktap", 'C.fdBlktap)
208 $(makeJSONInstance ''FileDriver)
210 -- | The persistent block driver type. Currently only one type is allowed.
211 $(declareSADT "BlockDriver"
212 [ ("BlockDrvManual", 'C.blockdevDriverManual)
214 $(makeJSONInstance ''BlockDriver)
216 -- | Constant for the dev_type key entry in the disk config.
220 -- | The disk configuration type. This includes the disk type itself,
221 -- for a more complete consistency. Note that since in the Python
222 -- code-base there's no authoritative place where we document the
223 -- logical id, this is probably a good reference point.
225 = LIDPlain String String -- ^ Volume group, logical volume
226 | LIDDrbd8 String String Int Int Int String
227 -- ^ NodeA, NodeB, Port, MinorA, MinorB, Secret
228 | LIDFile FileDriver String -- ^ Driver, path
229 | LIDBlockDev BlockDriver String -- ^ Driver, path (must be under /dev)
230 | LIDRados String String -- ^ Unused, path
231 deriving (Read, Show, Eq)
233 -- | Mapping from a logical id to a disk type.
234 lidDiskType :: DiskLogicalId -> DiskType
235 lidDiskType (LIDPlain {}) = LD_LV
236 lidDiskType (LIDDrbd8 {}) = LD_DRBD8
237 lidDiskType (LIDFile {}) = LD_FILE
238 lidDiskType (LIDBlockDev {}) = LD_BLOCKDEV
239 lidDiskType (LIDRados {}) = LD_RADOS
241 -- | Builds the extra disk_type field for a given logical id.
242 lidEncodeType :: DiskLogicalId -> [(String, JSValue)]
243 lidEncodeType v = [(devType, showJSON . lidDiskType $ v)]
245 -- | Custom encoder for DiskLogicalId (logical id only).
246 encodeDLId :: DiskLogicalId -> JSValue
247 encodeDLId (LIDPlain vg lv) = JSArray [showJSON vg, showJSON lv]
248 encodeDLId (LIDDrbd8 nodeA nodeB port minorA minorB key) =
249 JSArray [ showJSON nodeA, showJSON nodeB, showJSON port
250 , showJSON minorA, showJSON minorB, showJSON key ]
251 encodeDLId (LIDRados pool name) = JSArray [showJSON pool, showJSON name]
252 encodeDLId (LIDFile driver name) = JSArray [showJSON driver, showJSON name]
253 encodeDLId (LIDBlockDev driver name) = JSArray [showJSON driver, showJSON name]
255 -- | Custom encoder for DiskLogicalId, composing both the logical id
256 -- and the extra disk_type field.
257 encodeFullDLId :: DiskLogicalId -> (JSValue, [(String, JSValue)])
258 encodeFullDLId v = (encodeDLId v, lidEncodeType v)
260 -- | Custom decoder for DiskLogicalId. This is manual for now, since
261 -- we don't have yet automation for separate-key style fields.
262 decodeDLId :: [(String, JSValue)] -> JSValue -> J.Result DiskLogicalId
263 decodeDLId obj lid = do
264 dtype <- fromObj obj devType
268 JSArray [nA, nB, p, mA, mB, k] -> do
275 return $ LIDDrbd8 nA' nB' p' mA' mB' k'
276 _ -> fail $ "Can't read logical_id for DRBD8 type"
279 JSArray [vg, lv] -> do
282 return $ LIDPlain vg' lv'
283 _ -> fail $ "Can't read logical_id for plain type"
286 JSArray [driver, path] -> do
287 driver' <- readJSON driver
288 path' <- readJSON path
289 return $ LIDFile driver' path'
290 _ -> fail $ "Can't read logical_id for file type"
293 JSArray [driver, path] -> do
294 driver' <- readJSON driver
295 path' <- readJSON path
296 return $ LIDBlockDev driver' path'
297 _ -> fail $ "Can't read logical_id for blockdev type"
300 JSArray [driver, path] -> do
301 driver' <- readJSON driver
302 path' <- readJSON path
303 return $ LIDRados driver' path'
304 _ -> fail $ "Can't read logical_id for rdb type"
306 -- | Disk data structure.
308 -- This is declared manually as it's a recursive structure, and our TH
309 -- code currently can't build it.
311 { diskLogicalId :: DiskLogicalId
312 -- , diskPhysicalId :: String
313 , diskChildren :: [Disk]
314 , diskIvName :: String
316 , diskMode :: DiskMode
317 } deriving (Read, Show, Eq)
319 $(buildObjectSerialisation "Disk"
320 [ customField 'decodeDLId 'encodeFullDLId $
321 simpleField "logical_id" [t| DiskLogicalId |]
322 -- , simpleField "physical_id" [t| String |]
323 , defaultField [| [] |] $ simpleField "children" [t| [Disk] |]
324 , defaultField [| "" |] $ simpleField "iv_name" [t| String |]
325 , simpleField "size" [t| Int |]
326 , defaultField [| DiskRdWr |] $ simpleField "mode" [t| DiskMode |]
329 -- * Hypervisor definitions
331 -- | This may be due to change when we add hypervisor parameters.
332 $(declareSADT "Hypervisor"
333 [ ( "Kvm", 'C.htKvm )
334 , ( "XenPvm", 'C.htXenPvm )
335 , ( "Chroot", 'C.htChroot )
336 , ( "XenHvm", 'C.htXenHvm )
337 , ( "Lxc", 'C.htLxc )
338 , ( "Fake", 'C.htFake )
340 $(makeJSONInstance ''Hypervisor)
342 -- * Instance definitions
344 -- | Instance disk template type. **Copied from HTools/Types.hs**
345 $(declareSADT "DiskTemplate"
346 [ ("DTDiskless", 'C.dtDiskless)
347 , ("DTFile", 'C.dtFile)
348 , ("DTSharedFile", 'C.dtSharedFile)
349 , ("DTPlain", 'C.dtPlain)
350 , ("DTBlock", 'C.dtBlock)
351 , ("DTDrbd8", 'C.dtDrbd8)
352 , ("DTRados", 'C.dtRbd)
354 $(makeJSONInstance ''DiskTemplate)
356 $(declareSADT "AdminState"
357 [ ("AdminOffline", 'C.adminstOffline)
358 , ("AdminDown", 'C.adminstDown)
359 , ("AdminUp", 'C.adminstUp)
361 $(makeJSONInstance ''AdminState)
363 $(buildParam "Be" "bep" $
364 [ simpleField "minmem" [t| Int |]
365 , simpleField "maxmem" [t| Int |]
366 , simpleField "vcpus" [t| Int |]
367 , simpleField "auto_balance" [t| Bool |]
370 $(buildObject "Instance" "inst" $
371 [ simpleField "name" [t| String |]
372 , simpleField "primary_node" [t| String |]
373 , simpleField "os" [t| String |]
374 , simpleField "hypervisor" [t| Hypervisor |]
375 , simpleField "hvparams" [t| HvParams |]
376 , simpleField "beparams" [t| PartialBeParams |]
377 , simpleField "osparams" [t| OsParams |]
378 , simpleField "admin_state" [t| AdminState |]
379 , simpleField "nics" [t| [PartialNic] |]
380 , simpleField "disks" [t| [Disk] |]
381 , simpleField "disk_template" [t| DiskTemplate |]
382 , optionalField $ simpleField "network_port" [t| Int |]
389 instance TimeStampObject Instance where
393 instance UuidObject Instance where
396 instance SerialNoObject Instance where
397 serialOf = instSerial
399 instance TagsObject Instance where
402 -- * IPolicy definitions
404 $(buildParam "ISpec" "ispec" $
405 [ simpleField C.ispecMemSize [t| Int |]
406 , simpleField C.ispecDiskSize [t| Int |]
407 , simpleField C.ispecDiskCount [t| Int |]
408 , simpleField C.ispecCpuCount [t| Int |]
409 , simpleField C.ispecSpindleUse [t| Int |]
412 -- | Custom partial ipolicy. This is not built via buildParam since it
413 -- has a special 2-level inheritance mode.
414 $(buildObject "PartialIPolicy" "ipolicy" $
415 [ renameField "MinSpecP" $ simpleField "min" [t| PartialISpecParams |]
416 , renameField "MaxSpecP" $ simpleField "max" [t| PartialISpecParams |]
417 , renameField "StdSpecP" $ simpleField "std" [t| PartialISpecParams |]
418 , optionalField . renameField "SpindleRatioP"
419 $ simpleField "spindle-ratio" [t| Double |]
420 , optionalField . renameField "VcpuRatioP"
421 $ simpleField "vcpu-ratio" [t| Double |]
422 , optionalField . renameField "DiskTemplatesP"
423 $ simpleField "disk-templates" [t| [DiskTemplate] |]
426 -- | Custom filled ipolicy. This is not built via buildParam since it
427 -- has a special 2-level inheritance mode.
428 $(buildObject "FilledIPolicy" "ipolicy" $
429 [ renameField "MinSpec" $ simpleField "min" [t| FilledISpecParams |]
430 , renameField "MaxSpec" $ simpleField "max" [t| FilledISpecParams |]
431 , renameField "StdSpec" $ simpleField "std" [t| FilledISpecParams |]
432 , simpleField "spindle-ratio" [t| Double |]
433 , simpleField "vcpu-ratio" [t| Double |]
434 , simpleField "disk-templates" [t| [DiskTemplate] |]
437 -- | Custom filler for the ipolicy types.
438 fillIPolicy :: FilledIPolicy -> PartialIPolicy -> FilledIPolicy
439 fillIPolicy (FilledIPolicy { ipolicyMinSpec = fmin
440 , ipolicyMaxSpec = fmax
441 , ipolicyStdSpec = fstd
442 , ipolicySpindleRatio = fspindleRatio
443 , ipolicyVcpuRatio = fvcpuRatio
444 , ipolicyDiskTemplates = fdiskTemplates})
445 (PartialIPolicy { ipolicyMinSpecP = pmin
446 , ipolicyMaxSpecP = pmax
447 , ipolicyStdSpecP = pstd
448 , ipolicySpindleRatioP = pspindleRatio
449 , ipolicyVcpuRatioP = pvcpuRatio
450 , ipolicyDiskTemplatesP = pdiskTemplates}) =
451 FilledIPolicy { ipolicyMinSpec = fillISpecParams fmin pmin
452 , ipolicyMaxSpec = fillISpecParams fmax pmax
453 , ipolicyStdSpec = fillISpecParams fstd pstd
454 , ipolicySpindleRatio = fromMaybe fspindleRatio pspindleRatio
455 , ipolicyVcpuRatio = fromMaybe fvcpuRatio pvcpuRatio
456 , ipolicyDiskTemplates = fromMaybe fdiskTemplates
459 -- * Node definitions
461 $(buildParam "ND" "ndp" $
462 [ simpleField "oob_program" [t| String |]
463 , simpleField "spindle_count" [t| Int |]
466 $(buildObject "Node" "node" $
467 [ simpleField "name" [t| String |]
468 , simpleField "primary_ip" [t| String |]
469 , simpleField "secondary_ip" [t| String |]
470 , simpleField "master_candidate" [t| Bool |]
471 , simpleField "offline" [t| Bool |]
472 , simpleField "drained" [t| Bool |]
473 , simpleField "group" [t| String |]
474 , simpleField "master_capable" [t| Bool |]
475 , simpleField "vm_capable" [t| Bool |]
476 , simpleField "ndparams" [t| PartialNDParams |]
477 , simpleField "powered" [t| Bool |]
484 instance TimeStampObject Node where
488 instance UuidObject Node where
491 instance SerialNoObject Node where
492 serialOf = nodeSerial
494 instance TagsObject Node where
497 -- * NodeGroup definitions
499 -- | The Group allocation policy type.
501 -- Note that the order of constructors is important as the automatic
502 -- Ord instance will order them in the order they are defined, so when
503 -- changing this data type be careful about the interaction with the
504 -- desired sorting order.
506 -- FIXME: COPIED from Types.hs; we need to eliminate this duplication later
507 $(declareSADT "AllocPolicy"
508 [ ("AllocPreferred", 'C.allocPolicyPreferred)
509 , ("AllocLastResort", 'C.allocPolicyLastResort)
510 , ("AllocUnallocable", 'C.allocPolicyUnallocable)
512 $(makeJSONInstance ''AllocPolicy)
514 -- | The disk parameters type.
515 type DiskParams = Container (Container JSValue)
517 $(buildObject "NodeGroup" "group" $
518 [ simpleField "name" [t| String |]
519 , defaultField [| [] |] $ simpleField "members" [t| [String] |]
520 , simpleField "ndparams" [t| PartialNDParams |]
521 , simpleField "alloc_policy" [t| AllocPolicy |]
522 , simpleField "ipolicy" [t| PartialIPolicy |]
523 , simpleField "diskparams" [t| DiskParams |]
530 instance TimeStampObject NodeGroup where
534 instance UuidObject NodeGroup where
537 instance SerialNoObject NodeGroup where
538 serialOf = groupSerial
540 instance TagsObject NodeGroup where
544 $(declareIADT "IpFamily"
545 [ ("IpFamilyV4", 'C.ip4Family)
546 , ("IpFamilyV6", 'C.ip6Family)
548 $(makeJSONInstance ''IpFamily)
550 -- | Conversion from IP family to IP version. This is needed because
551 -- Python uses both, depending on context.
552 ipFamilyToVersion :: IpFamily -> Int
553 ipFamilyToVersion IpFamilyV4 = C.ip4Version
554 ipFamilyToVersion IpFamilyV6 = C.ip6Version
556 -- | Cluster HvParams (hvtype to hvparams mapping).
557 type ClusterHvParams = Container HvParams
559 -- | Cluster Os-HvParams (os to hvparams mapping).
560 type OsHvParams = Container ClusterHvParams
562 -- | Cluser BeParams.
563 type ClusterBeParams = Container FilledBeParams
565 -- | Cluster OsParams.
566 type ClusterOsParams = Container OsParams
568 -- | Cluster NicParams.
569 type ClusterNicParams = Container FilledNicParams
571 -- | Cluster UID Pool, list (low, high) UID ranges.
572 type UidPool = [(Int, Int)]
574 -- * Cluster definitions
575 $(buildObject "Cluster" "cluster" $
576 [ simpleField "rsahostkeypub" [t| String |]
577 , simpleField "highest_used_port" [t| Int |]
578 , simpleField "tcpudp_port_pool" [t| [Int] |]
579 , simpleField "mac_prefix" [t| String |]
580 , simpleField "volume_group_name" [t| String |]
581 , simpleField "reserved_lvs" [t| [String] |]
583 simpleField "drbd_usermode_helper" [t| String |]
584 , simpleField "master_node" [t| String |]
585 , simpleField "master_ip" [t| String |]
586 , simpleField "master_netdev" [t| String |]
587 , simpleField "master_netmask" [t| Int |]
588 , simpleField "use_external_mip_script" [t| Bool |]
589 , simpleField "cluster_name" [t| String |]
590 , simpleField "file_storage_dir" [t| String |]
591 , simpleField "shared_file_storage_dir" [t| String |]
592 , simpleField "enabled_hypervisors" [t| [String] |]
593 , simpleField "hvparams" [t| ClusterHvParams |]
594 , simpleField "os_hvp" [t| OsHvParams |]
595 , simpleField "beparams" [t| ClusterBeParams |]
596 , simpleField "osparams" [t| ClusterOsParams |]
597 , simpleField "nicparams" [t| ClusterNicParams |]
598 , simpleField "ndparams" [t| FilledNDParams |]
599 , simpleField "diskparams" [t| DiskParams |]
600 , simpleField "candidate_pool_size" [t| Int |]
601 , simpleField "modify_etc_hosts" [t| Bool |]
602 , simpleField "modify_ssh_setup" [t| Bool |]
603 , simpleField "maintain_node_health" [t| Bool |]
604 , simpleField "uid_pool" [t| UidPool |]
605 , simpleField "default_iallocator" [t| String |]
606 , simpleField "hidden_os" [t| [String] |]
607 , simpleField "blacklisted_os" [t| [String] |]
608 , simpleField "primary_ip_family" [t| IpFamily |]
609 , simpleField "prealloc_wipe_disks" [t| Bool |]
610 , simpleField "ipolicy" [t| FilledIPolicy |]
617 instance TimeStampObject Cluster where
618 cTimeOf = clusterCtime
619 mTimeOf = clusterMtime
621 instance UuidObject Cluster where
624 instance SerialNoObject Cluster where
625 serialOf = clusterSerial
627 instance TagsObject Cluster where
630 -- * ConfigData definitions
632 $(buildObject "ConfigData" "config" $
633 -- timeStampFields ++
634 [ simpleField "version" [t| Int |]
635 , simpleField "cluster" [t| Cluster |]
636 , simpleField "nodes" [t| Container Node |]
637 , simpleField "nodegroups" [t| Container NodeGroup |]
638 , simpleField "instances" [t| Container Instance |]
642 instance SerialNoObject ConfigData where
643 serialOf = configSerial