1 {-# LANGUAGE TemplateHaskell #-}
3 {-| Some common Ganeti types.
5 This holds types common to both core work, and to htools. Types that
6 are very core specific (e.g. configuration objects) should go in
7 'Ganeti.Objects', while types that are specific to htools in-memory
8 representation should go into 'Ganeti.HTools.Types'.
14 Copyright (C) 2012, 2013 Google Inc.
16 This program is free software; you can redistribute it and/or modify
17 it under the terms of the GNU General Public License as published by
18 the Free Software Foundation; either version 2 of the License, or
19 (at your option) any later version.
21 This program is distributed in the hope that it will be useful, but
22 WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 General Public License for more details.
26 You should have received a copy of the GNU General Public License
27 along with this program; if not, write to the Free Software
28 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
38 , instanceStatusFromRaw
70 , VerifyOptionalChecks(..)
71 , verifyOptionalChecksToRaw
93 , IAllocatorTestDir(..)
94 , iAllocatorTestDirToRaw
102 , FinalizedJobStatus(..)
103 , finalizedJobStatusToRaw
111 , OpSubmitPriority(..)
112 , opSubmitPriorityToRaw
113 , parseSubmitPriority
125 , addParamsToStorageUnit
126 , diskTemplateToStorageType
129 import Control.Monad (liftM)
130 import qualified Text.JSON as JSON
131 import Text.JSON (JSON, readJSON, showJSON)
132 import Data.Ratio (numerator, denominator)
134 import qualified Ganeti.ConstantUtils as ConstantUtils
136 import qualified Ganeti.THH as THH
141 -- | Type that holds a non-negative value.
142 newtype NonNegative a = NonNegative { fromNonNegative :: a }
145 -- | Smart constructor for 'NonNegative'.
146 mkNonNegative :: (Monad m, Num a, Ord a, Show a) => a -> m (NonNegative a)
147 mkNonNegative i | i >= 0 = return (NonNegative i)
148 | otherwise = fail $ "Invalid value for non-negative type '" ++
151 instance (JSON.JSON a, Num a, Ord a, Show a) => JSON.JSON (NonNegative a) where
152 showJSON = JSON.showJSON . fromNonNegative
153 readJSON v = JSON.readJSON v >>= mkNonNegative
155 -- | Type that holds a positive value.
156 newtype Positive a = Positive { fromPositive :: a }
159 -- | Smart constructor for 'Positive'.
160 mkPositive :: (Monad m, Num a, Ord a, Show a) => a -> m (Positive a)
161 mkPositive i | i > 0 = return (Positive i)
162 | otherwise = fail $ "Invalid value for positive type '" ++
165 instance (JSON.JSON a, Num a, Ord a, Show a) => JSON.JSON (Positive a) where
166 showJSON = JSON.showJSON . fromPositive
167 readJSON v = JSON.readJSON v >>= mkPositive
169 -- | Type that holds a negative value.
170 newtype Negative a = Negative { fromNegative :: a }
173 -- | Smart constructor for 'Negative'.
174 mkNegative :: (Monad m, Num a, Ord a, Show a) => a -> m (Negative a)
175 mkNegative i | i < 0 = return (Negative i)
176 | otherwise = fail $ "Invalid value for negative type '" ++
179 instance (JSON.JSON a, Num a, Ord a, Show a) => JSON.JSON (Negative a) where
180 showJSON = JSON.showJSON . fromNegative
181 readJSON v = JSON.readJSON v >>= mkNegative
183 -- | Type that holds a non-null list.
184 newtype NonEmpty a = NonEmpty { fromNonEmpty :: [a] }
187 -- | Smart constructor for 'NonEmpty'.
188 mkNonEmpty :: (Monad m) => [a] -> m (NonEmpty a)
189 mkNonEmpty [] = fail "Received empty value for non-empty list"
190 mkNonEmpty xs = return (NonEmpty xs)
192 instance (Eq a, Ord a) => Ord (NonEmpty a) where
193 NonEmpty { fromNonEmpty = x1 } `compare` NonEmpty { fromNonEmpty = x2 } =
196 instance (JSON.JSON a) => JSON.JSON (NonEmpty a) where
197 showJSON = JSON.showJSON . fromNonEmpty
198 readJSON v = JSON.readJSON v >>= mkNonEmpty
200 -- | A simple type alias for non-empty strings.
201 type NonEmptyString = NonEmpty Char
203 type QueryResultCode = Int
205 newtype IPv4Address = IPv4Address { fromIPv4Address :: String }
208 -- FIXME: this should check that 'address' is a valid ip
209 mkIPv4Address :: Monad m => String -> m IPv4Address
210 mkIPv4Address address =
211 return IPv4Address { fromIPv4Address = address }
213 instance JSON.JSON IPv4Address where
214 showJSON = JSON.showJSON . fromIPv4Address
215 readJSON v = JSON.readJSON v >>= mkIPv4Address
217 newtype IPv4Network = IPv4Network { fromIPv4Network :: String }
220 -- FIXME: this should check that 'address' is a valid ip
221 mkIPv4Network :: Monad m => String -> m IPv4Network
222 mkIPv4Network address =
223 return IPv4Network { fromIPv4Network = address }
225 instance JSON.JSON IPv4Network where
226 showJSON = JSON.showJSON . fromIPv4Network
227 readJSON v = JSON.readJSON v >>= mkIPv4Network
229 newtype IPv6Address = IPv6Address { fromIPv6Address :: String }
232 -- FIXME: this should check that 'address' is a valid ip
233 mkIPv6Address :: Monad m => String -> m IPv6Address
234 mkIPv6Address address =
235 return IPv6Address { fromIPv6Address = address }
237 instance JSON.JSON IPv6Address where
238 showJSON = JSON.showJSON . fromIPv6Address
239 readJSON v = JSON.readJSON v >>= mkIPv6Address
241 newtype IPv6Network = IPv6Network { fromIPv6Network :: String }
244 -- FIXME: this should check that 'address' is a valid ip
245 mkIPv6Network :: Monad m => String -> m IPv6Network
246 mkIPv6Network address =
247 return IPv6Network { fromIPv6Network = address }
249 instance JSON.JSON IPv6Network where
250 showJSON = JSON.showJSON . fromIPv6Network
251 readJSON v = JSON.readJSON v >>= mkIPv6Network
255 -- | Instance disk template type.
256 $(THH.declareLADT ''String "DiskTemplate"
257 [ ("DTDiskless", "diskless")
259 , ("DTSharedFile", "sharedfile")
260 , ("DTPlain", "plain")
261 , ("DTBlock", "blockdev")
262 , ("DTDrbd8", "drbd")
266 $(THH.makeJSONInstance ''DiskTemplate)
268 instance HasStringRepr DiskTemplate where
269 fromStringRepr = diskTemplateFromRaw
270 toStringRepr = diskTemplateToRaw
272 -- | Data type representing what items the tag operations apply to.
273 $(THH.declareLADT ''String "TagKind"
274 [ ("TagKindInstance", "instance")
275 , ("TagKindNode", "node")
276 , ("TagKindGroup", "nodegroup")
277 , ("TagKindCluster", "cluster")
279 $(THH.makeJSONInstance ''TagKind)
281 -- | The Group allocation policy type.
283 -- Note that the order of constructors is important as the automatic
284 -- Ord instance will order them in the order they are defined, so when
285 -- changing this data type be careful about the interaction with the
286 -- desired sorting order.
287 $(THH.declareLADT ''String "AllocPolicy"
288 [ ("AllocPreferred", "preferred")
289 , ("AllocLastResort", "last_resort")
290 , ("AllocUnallocable", "unallocable")
292 $(THH.makeJSONInstance ''AllocPolicy)
294 -- | The Instance real state type. FIXME: this could be improved to
295 -- just wrap a /NormalState AdminStatus | ErrorState ErrorCondition/.
296 $(THH.declareLADT ''String "InstanceStatus"
297 [ ("StatusDown", "ADMIN_down")
298 , ("StatusOffline", "ADMIN_offline")
299 , ("ErrorDown", "ERROR_down")
300 , ("ErrorUp", "ERROR_up")
301 , ("NodeDown", "ERROR_nodedown")
302 , ("NodeOffline", "ERROR_nodeoffline")
303 , ("Running", "running")
304 , ("WrongNode", "ERROR_wrongnode")
306 $(THH.makeJSONInstance ''InstanceStatus)
309 $(THH.declareLADT ''String "MigrationMode"
310 [ ("MigrationLive", "live")
311 , ("MigrationNonLive", "non-live")
313 $(THH.makeJSONInstance ''MigrationMode)
315 -- | Verify optional checks.
316 $(THH.declareLADT ''String "VerifyOptionalChecks"
317 [ ("VerifyNPlusOneMem", "nplusone_mem")
319 $(THH.makeJSONInstance ''VerifyOptionalChecks)
321 -- | Cluster verify error codes.
322 $(THH.declareLADT ''String "CVErrorCode"
323 [ ("CvECLUSTERCFG", "ECLUSTERCFG")
324 , ("CvECLUSTERCERT", "ECLUSTERCERT")
325 , ("CvECLUSTERFILECHECK", "ECLUSTERFILECHECK")
326 , ("CvECLUSTERDANGLINGNODES", "ECLUSTERDANGLINGNODES")
327 , ("CvECLUSTERDANGLINGINST", "ECLUSTERDANGLINGINST")
328 , ("CvEINSTANCEBADNODE", "EINSTANCEBADNODE")
329 , ("CvEINSTANCEDOWN", "EINSTANCEDOWN")
330 , ("CvEINSTANCELAYOUT", "EINSTANCELAYOUT")
331 , ("CvEINSTANCEMISSINGDISK", "EINSTANCEMISSINGDISK")
332 , ("CvEINSTANCEFAULTYDISK", "EINSTANCEFAULTYDISK")
333 , ("CvEINSTANCEWRONGNODE", "EINSTANCEWRONGNODE")
334 , ("CvEINSTANCESPLITGROUPS", "EINSTANCESPLITGROUPS")
335 , ("CvEINSTANCEPOLICY", "EINSTANCEPOLICY")
336 , ("CvENODEDRBD", "ENODEDRBD")
337 , ("CvENODEDRBDHELPER", "ENODEDRBDHELPER")
338 , ("CvENODEFILECHECK", "ENODEFILECHECK")
339 , ("CvENODEHOOKS", "ENODEHOOKS")
340 , ("CvENODEHV", "ENODEHV")
341 , ("CvENODELVM", "ENODELVM")
342 , ("CvENODEN1", "ENODEN1")
343 , ("CvENODENET", "ENODENET")
344 , ("CvENODEOS", "ENODEOS")
345 , ("CvENODEORPHANINSTANCE", "ENODEORPHANINSTANCE")
346 , ("CvENODEORPHANLV", "ENODEORPHANLV")
347 , ("CvENODERPC", "ENODERPC")
348 , ("CvENODESSH", "ENODESSH")
349 , ("CvENODEVERSION", "ENODEVERSION")
350 , ("CvENODESETUP", "ENODESETUP")
351 , ("CvENODETIME", "ENODETIME")
352 , ("CvENODEOOBPATH", "ENODEOOBPATH")
353 , ("CvENODEUSERSCRIPTS", "ENODEUSERSCRIPTS")
354 , ("CvENODEFILESTORAGEPATHS", "ENODEFILESTORAGEPATHS")
355 , ("CvENODEFILESTORAGEPATHUNUSABLE", "ENODEFILESTORAGEPATHUNUSABLE")
356 , ("CvENODESHAREDFILESTORAGEPATHUNUSABLE",
357 "ENODESHAREDFILESTORAGEPATHUNUSABLE")
359 $(THH.makeJSONInstance ''CVErrorCode)
361 -- | Dynamic device modification, just add\/remove version.
362 $(THH.declareLADT ''String "DdmSimple"
363 [ ("DdmSimpleAdd", "add")
364 , ("DdmSimpleRemove", "remove")
366 $(THH.makeJSONInstance ''DdmSimple)
368 -- | Dynamic device modification, all operations version.
369 $(THH.declareLADT ''String "DdmFull"
370 [ ("DdmFullAdd", "add")
371 , ("DdmFullRemove", "remove")
372 , ("DdmFullModify", "modify")
374 $(THH.makeJSONInstance ''DdmFull)
376 -- | Hypervisor type definitions.
377 $(THH.declareLADT ''String "Hypervisor"
379 , ("XenPvm", "xen-pvm")
380 , ("Chroot", "chroot")
381 , ("XenHvm", "xen-hvm")
385 $(THH.makeJSONInstance ''Hypervisor)
387 -- | Oob command type.
388 $(THH.declareLADT ''String "OobCommand"
389 [ ("OobHealth", "health")
390 , ("OobPowerCycle", "power-cycle")
391 , ("OobPowerOff", "power-off")
392 , ("OobPowerOn", "power-on")
393 , ("OobPowerStatus", "power-status")
395 $(THH.makeJSONInstance ''OobCommand)
398 $(THH.declareLADT ''String "StorageType"
399 [ ("StorageFile", "file")
400 , ("StorageLvmPv", "lvm-pv")
401 , ("StorageLvmVg", "lvm-vg")
402 , ("StorageDiskless", "diskless")
403 , ("StorageBlock", "blockdev")
404 , ("StorageRados", "rados")
405 , ("StorageExt", "ext")
407 $(THH.makeJSONInstance ''StorageType)
409 -- | Storage keys are identifiers for storage units. Their content varies
410 -- depending on the storage type, for example a storage key for LVM storage
411 -- is the volume group name.
412 type StorageKey = String
414 -- | Storage parameters
415 type SPExclusiveStorage = Bool
417 -- | Storage units without storage-type-specific parameters
418 data StorageUnitRaw = SURaw StorageType StorageKey
420 -- | Full storage unit with storage-type-specific parameters
421 data StorageUnit = SUFile StorageKey
422 | SULvmPv StorageKey SPExclusiveStorage
423 | SULvmVg StorageKey SPExclusiveStorage
424 | SUDiskless StorageKey
430 instance Show StorageUnit where
431 show (SUFile key) = showSUSimple StorageFile key
432 show (SULvmPv key es) = showSULvm StorageLvmPv key es
433 show (SULvmVg key es) = showSULvm StorageLvmVg key es
434 show (SUDiskless key) = showSUSimple StorageDiskless key
435 show (SUBlock key) = showSUSimple StorageBlock key
436 show (SURados key) = showSUSimple StorageRados key
437 show (SUExt key) = showSUSimple StorageExt key
439 instance JSON StorageUnit where
440 showJSON (SUFile key) = showJSON (StorageFile, key, []::[String])
441 showJSON (SULvmPv key es) = showJSON (StorageLvmPv, key, [es])
442 showJSON (SULvmVg key es) = showJSON (StorageLvmVg, key, [es])
443 showJSON (SUDiskless key) = showJSON (StorageDiskless, key, []::[String])
444 showJSON (SUBlock key) = showJSON (StorageBlock, key, []::[String])
445 showJSON (SURados key) = showJSON (StorageRados, key, []::[String])
446 showJSON (SUExt key) = showJSON (StorageExt, key, []::[String])
447 -- FIXME: add readJSON implementation
448 readJSON = fail "Not implemented"
450 -- | Composes a string representation of storage types without
451 -- storage parameters
452 showSUSimple :: StorageType -> StorageKey -> String
453 showSUSimple st sk = show (storageTypeToRaw st, sk, []::[String])
455 -- | Composes a string representation of the LVM storage types
456 showSULvm :: StorageType -> StorageKey -> SPExclusiveStorage -> String
457 showSULvm st sk es = show (storageTypeToRaw st, sk, [es])
459 -- | Mapping from disk templates to storage types
460 -- FIXME: This is semantically the same as the constant
461 -- C.diskTemplatesStorageType, remove this when python constants
462 -- are generated from haskell constants
463 diskTemplateToStorageType :: DiskTemplate -> StorageType
464 diskTemplateToStorageType DTExt = StorageExt
465 diskTemplateToStorageType DTFile = StorageFile
466 diskTemplateToStorageType DTSharedFile = StorageFile
467 diskTemplateToStorageType DTDrbd8 = StorageLvmVg
468 diskTemplateToStorageType DTPlain = StorageLvmVg
469 diskTemplateToStorageType DTRbd = StorageRados
470 diskTemplateToStorageType DTDiskless = StorageDiskless
471 diskTemplateToStorageType DTBlock = StorageBlock
473 -- | Equips a raw storage unit with its parameters
474 addParamsToStorageUnit :: SPExclusiveStorage -> StorageUnitRaw -> StorageUnit
475 addParamsToStorageUnit _ (SURaw StorageBlock key) = SUBlock key
476 addParamsToStorageUnit _ (SURaw StorageDiskless key) = SUDiskless key
477 addParamsToStorageUnit _ (SURaw StorageExt key) = SUExt key
478 addParamsToStorageUnit _ (SURaw StorageFile key) = SUFile key
479 addParamsToStorageUnit es (SURaw StorageLvmPv key) = SULvmPv key es
480 addParamsToStorageUnit es (SURaw StorageLvmVg key) = SULvmVg key es
481 addParamsToStorageUnit _ (SURaw StorageRados key) = SURados key
483 -- | Node evac modes.
484 $(THH.declareLADT ''String "NodeEvacMode"
485 [ ("NEvacPrimary", "primary-only")
486 , ("NEvacSecondary", "secondary-only")
487 , ("NEvacAll", "all")
489 $(THH.makeJSONInstance ''NodeEvacMode)
491 -- | The file driver type.
492 $(THH.declareLADT ''String "FileDriver"
493 [ ("FileLoop", "loop")
494 , ("FileBlktap", "blktap")
496 $(THH.makeJSONInstance ''FileDriver)
498 -- | The instance create mode.
499 $(THH.declareLADT ''String "InstCreateMode"
500 [ ("InstCreate", "create")
501 , ("InstImport", "import")
502 , ("InstRemoteImport", "remote-import")
504 $(THH.makeJSONInstance ''InstCreateMode)
507 $(THH.declareLADT ''String "RebootType"
508 [ ("RebootSoft", "soft")
509 , ("RebootHard", "hard")
510 , ("RebootFull", "full")
512 $(THH.makeJSONInstance ''RebootType)
515 $(THH.declareLADT ''String "ExportMode"
516 [ ("ExportModeLocal", "local")
517 , ("ExportModeRemote", "remote")
519 $(THH.makeJSONInstance ''ExportMode)
521 -- | IAllocator run types (OpTestIAllocator).
522 $(THH.declareLADT ''String "IAllocatorTestDir"
523 [ ("IAllocatorDirIn", "in")
524 , ("IAllocatorDirOut", "out")
526 $(THH.makeJSONInstance ''IAllocatorTestDir)
528 -- | IAllocator mode. FIXME: use this in "HTools.Backend.IAlloc".
529 $(THH.declareLADT ''String "IAllocatorMode"
530 [ ("IAllocatorAlloc", "allocate")
531 , ("IAllocatorMultiAlloc", "multi-allocate")
532 , ("IAllocatorReloc", "relocate")
533 , ("IAllocatorNodeEvac", "node-evacuate")
534 , ("IAllocatorChangeGroup", "change-group")
536 $(THH.makeJSONInstance ''IAllocatorMode)
539 $(THH.declareLADT ''String "NICMode"
540 [ ("NMBridged", "bridged")
541 , ("NMRouted", "routed")
542 , ("NMOvs", "openvswitch")
545 $(THH.makeJSONInstance ''NICMode)
547 -- | The JobStatus data type. Note that this is ordered especially
548 -- such that greater\/lesser comparison on values of this type makes
550 $(THH.declareLADT ''String "JobStatus"
551 [ ("JOB_STATUS_QUEUED", "queued")
552 , ("JOB_STATUS_WAITING", "waiting")
553 , ("JOB_STATUS_CANCELING", "canceling")
554 , ("JOB_STATUS_RUNNING", "running")
555 , ("JOB_STATUS_CANCELED", "canceled")
556 , ("JOB_STATUS_SUCCESS", "success")
557 , ("JOB_STATUS_ERROR", "error")
559 $(THH.makeJSONInstance ''JobStatus)
561 -- | Finalized job status.
562 $(THH.declareLADT ''String "FinalizedJobStatus"
563 [ ("JobStatusCanceled", "canceled")
564 , ("JobStatusSuccessful", "success")
565 , ("JobStatusFailed", "error")
567 $(THH.makeJSONInstance ''FinalizedJobStatus)
569 -- | The Ganeti job type.
570 newtype JobId = JobId { fromJobId :: Int }
573 -- | Builds a job ID.
574 makeJobId :: (Monad m) => Int -> m JobId
575 makeJobId i | i >= 0 = return $ JobId i
576 | otherwise = fail $ "Invalid value for job ID ' " ++ show i ++ "'"
578 -- | Builds a job ID from a string.
579 makeJobIdS :: (Monad m) => String -> m JobId
580 makeJobIdS s = tryRead "parsing job id" s >>= makeJobId
582 -- | Parses a job ID.
583 parseJobId :: (Monad m) => JSON.JSValue -> m JobId
584 parseJobId (JSON.JSString x) = makeJobIdS $ JSON.fromJSString x
585 parseJobId (JSON.JSRational _ x) =
586 if denominator x /= 1
587 then fail $ "Got fractional job ID from master daemon?! Value:" ++ show x
588 -- FIXME: potential integer overflow here on 32-bit platforms
589 else makeJobId . fromIntegral . numerator $ x
590 parseJobId x = fail $ "Wrong type/value for job id: " ++ show x
592 instance JSON.JSON JobId where
593 showJSON = JSON.showJSON . fromJobId
594 readJSON = parseJobId
596 -- | Relative job ID type alias.
597 type RelativeJobId = Negative Int
599 -- | Job ID dependency.
600 data JobIdDep = JobDepRelative RelativeJobId
601 | JobDepAbsolute JobId
604 instance JSON.JSON JobIdDep where
605 showJSON (JobDepRelative i) = showJSON i
606 showJSON (JobDepAbsolute i) = showJSON i
608 case JSON.readJSON v::JSON.Result (Negative Int) of
609 -- first try relative dependency, usually most common
610 JSON.Ok r -> return $ JobDepRelative r
611 JSON.Error _ -> liftM JobDepAbsolute (parseJobId v)
613 -- | Job Dependency type.
614 data JobDependency = JobDependency JobIdDep [FinalizedJobStatus]
617 instance JSON JobDependency where
618 showJSON (JobDependency dep status) = showJSON (dep, status)
619 readJSON = liftM (uncurry JobDependency) . readJSON
621 -- | Valid opcode priorities for submit.
622 $(THH.declareIADT "OpSubmitPriority"
623 [ ("OpPrioLow", 'ConstantUtils.priorityLow)
624 , ("OpPrioNormal", 'ConstantUtils.priorityNormal)
625 , ("OpPrioHigh", 'ConstantUtils.priorityHigh)
627 $(THH.makeJSONInstance ''OpSubmitPriority)
629 -- | Parse submit priorities from a string.
630 parseSubmitPriority :: (Monad m) => String -> m OpSubmitPriority
631 parseSubmitPriority "low" = return OpPrioLow
632 parseSubmitPriority "normal" = return OpPrioNormal
633 parseSubmitPriority "high" = return OpPrioHigh
634 parseSubmitPriority str = fail $ "Unknown priority '" ++ str ++ "'"
636 -- | Format a submit priority as string.
637 fmtSubmitPriority :: OpSubmitPriority -> String
638 fmtSubmitPriority OpPrioLow = "low"
639 fmtSubmitPriority OpPrioNormal = "normal"
640 fmtSubmitPriority OpPrioHigh = "high"
642 -- | Our ADT for the OpCode status at runtime (while in a job).
643 $(THH.declareLADT ''String "OpStatus"
644 [ ("OP_STATUS_QUEUED", "queued")
645 , ("OP_STATUS_WAITING", "waiting")
646 , ("OP_STATUS_CANCELING", "canceling")
647 , ("OP_STATUS_RUNNING", "running")
648 , ("OP_STATUS_CANCELED", "canceled")
649 , ("OP_STATUS_SUCCESS", "success")
650 , ("OP_STATUS_ERROR", "error")
652 $(THH.makeJSONInstance ''OpStatus)
654 -- | Type for the job message type.
655 $(THH.declareLADT ''String "ELogType"
656 [ ("ELogMessage", "message")
657 , ("ELogRemoteImport", "remote-import")
658 , ("ELogJqueueTest", "jqueue-test")
660 $(THH.makeJSONInstance ''ELogType)
662 -- | Type of one element of a reason trail.
663 type ReasonElem = (String, String, Integer)
665 -- | Type representing a reason trail.
666 type ReasonTrail = [ReasonElem]