As per our design, the maintenance daemons stores its state in the configuration; also, various of its aspects are configurable. So add a corresponding configuration object.
Signed-off-by: Klaus Aehlig <[email protected]> --- Makefile.am | 1 + lib/bootstrap.py | 2 ++ lib/objects.py | 21 +++++++++++++- lib/tools/cfgupgrade.py | 13 ++++++++- src/Ganeti/Objects.hs | 3 ++ src/Ganeti/Objects/Lens.hs | 8 ++++++ src/Ganeti/Objects/Maintenance.hs | 59 +++++++++++++++++++++++++++++++++++++++ test/hs/Test/Ganeti/Objects.hs | 10 ++++++- test/py/cfgupgrade_unittest.py | 1 + 9 files changed, 115 insertions(+), 3 deletions(-) create mode 100644 src/Ganeti/Objects/Maintenance.hs diff --git a/Makefile.am b/Makefile.am index 57760c3..bd5b64b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -983,6 +983,7 @@ HS_LIB_SRCS = \ src/Ganeti/Objects/Disk.hs \ src/Ganeti/Objects/Instance.hs \ src/Ganeti/Objects/Lens.hs \ + src/Ganeti/Objects/Maintenance.hs \ src/Ganeti/Objects/Nic.hs \ src/Ganeti/OpCodes.hs \ src/Ganeti/OpCodes/Lens.hs \ diff --git a/lib/bootstrap.py b/lib/bootstrap.py index 1164e5e..9fc0231 100644 --- a/lib/bootstrap.py +++ b/lib/bootstrap.py @@ -863,6 +863,7 @@ def InitConfig(version, cluster_config, master_node_config, default_nodegroup.uuid: default_nodegroup, } now = time.time() + maintenance = objects.Maintenance(serial_no=1, ctime=now, mtime=now) config_data = objects.ConfigData(version=version, cluster=cluster_config, nodegroups=nodegroups, @@ -871,6 +872,7 @@ def InitConfig(version, cluster_config, master_node_config, networks={}, disks={}, filters={}, + maintenance=maintenance, serial_no=1, ctime=now, mtime=now) utils.WriteFile(cfg_file, diff --git a/lib/objects.py b/lib/objects.py index 0ce3c0f..806fe75 100644 --- a/lib/objects.py +++ b/lib/objects.py @@ -63,7 +63,7 @@ from socket import AF_INET __all__ = ["ConfigObject", "ConfigData", "NIC", "Disk", "Instance", "OS", "Node", "NodeGroup", "Cluster", "FillDict", "Network", - "Filter"] + "Filter", "Maintenance"] _TIMESTAMPS = ["ctime", "mtime"] _UUID = ["uuid"] @@ -416,6 +416,7 @@ class ConfigData(ConfigObject): "networks", "disks", "filters", + "maintenance", "serial_no", ] + _TIMESTAMPS @@ -428,6 +429,7 @@ class ConfigData(ConfigObject): """ mydict = super(ConfigData, self).ToDict(_with_private=_with_private) mydict["cluster"] = mydict["cluster"].ToDict() + mydict["maintenance"] = mydict["maintenance"].ToDict() for key in ("nodes", "instances", "nodegroups", "networks", "disks", "filters"): mydict[key] = outils.ContainerToDicts(mydict[key]) @@ -449,6 +451,7 @@ class ConfigData(ConfigObject): obj.networks = outils.ContainerFromDicts(obj.networks, dict, Network) obj.disks = outils.ContainerFromDicts(obj.disks, dict, Disk) obj.filters = outils.ContainerFromDicts(obj.filters, dict, Filter) + obj.maintenance = Maintenance.FromDict(obj.maintenance) return obj def DisksOfType(self, dev_type): @@ -491,6 +494,9 @@ class ConfigData(ConfigObject): disk.UpgradeConfig() if self.filters is None: self.filters = {} + if self.maintenance is None: + self.maintenance = Maintenance.FromDict({}) + self.maintenance.UpgradeConfig() def _UpgradeEnabledDiskTemplates(self): """Upgrade the cluster's enabled disk templates by inspecting the currently @@ -549,6 +555,19 @@ class Filter(ConfigObject): "predicates", "action", "reason_trail"] + _UUID +class Maintenance(ConfigObject): + """Config object representing the state of the maintenance daemon""" + __slots__ = ["roundDelay", "jobs", "serial_no"] + _TIMESTAMPS + + def UpgradeConfig(self): + if self.serial_no is None: + self.serial_no = 1 + if self.mtime is None: + self.mtime = time.time() + if self.ctime is None: + self.ctime = time.time() + + class Disk(ConfigObject): """Config object representing a block device.""" __slots__ = [ diff --git a/lib/tools/cfgupgrade.py b/lib/tools/cfgupgrade.py index d64c7f9..90fe2c9 100644 --- a/lib/tools/cfgupgrade.py +++ b/lib/tools/cfgupgrade.py @@ -677,6 +677,14 @@ class CfgUpgrade(object): else: disk["nodes"] = [] + @OrFail("Upgrading maintenance data") + def UpgradeMaintenance(self): + # pylint can't infer config_data type + # pylint: disable=E1103 + maintenance = self.config_data.get("maintenance", None) + if maintenance is None: + self.config_data["maintenance"] = {} + def UpgradeAll(self): self.config_data["version"] = version.BuildVersion(TARGET_MAJOR, TARGET_MINOR, 0) @@ -692,7 +700,8 @@ class CfgUpgrade(object): self.UpgradeInstanceIndices, self.UpgradeFilters, self.UpgradeDiskNodes, - self.UpgradeDiskTemplate] + self.UpgradeDiskTemplate, + self.UpgradeMaintenance] for s in steps: s() return not self.errors @@ -700,6 +709,8 @@ class CfgUpgrade(object): # DOWNGRADE ------------------------------------------------------------ def DowngradeAll(self): + if "maintenance" in self.config_data: + del self.config_data["maintenance"] self.config_data["version"] = version.BuildVersion(DOWNGRADE_MAJOR, DOWNGRADE_MINOR, 0) return True diff --git a/src/Ganeti/Objects.hs b/src/Ganeti/Objects.hs index 2bf734f..ee4fd70 100644 --- a/src/Ganeti/Objects.hs +++ b/src/Ganeti/Objects.hs @@ -103,6 +103,7 @@ module Ganeti.Objects , module Ganeti.PartialParams , module Ganeti.Objects.Disk , module Ganeti.Objects.Instance + , module Ganeti.Objects.Maintenance ) where import Control.Applicative @@ -126,6 +127,7 @@ import qualified Ganeti.ConstantUtils as ConstantUtils import Ganeti.JSON import Ganeti.Objects.BitArray (BitArray) import Ganeti.Objects.Disk +import Ganeti.Objects.Maintenance import Ganeti.Objects.Nic import Ganeti.Objects.Instance import Ganeti.Query.Language @@ -701,6 +703,7 @@ $(buildObject "ConfigData" "config" $ , simpleField "networks" [t| Container Network |] , simpleField "disks" [t| Container Disk |] , simpleField "filters" [t| Container FilterRule |] + , simpleField "maintenance" [t| MaintenanceData |] ] ++ timeStampFields ++ serialFields) diff --git a/src/Ganeti/Objects/Lens.hs b/src/Ganeti/Objects/Lens.hs index 05bb5f2..8850dc9 100644 --- a/src/Ganeti/Objects/Lens.hs +++ b/src/Ganeti/Objects/Lens.hs @@ -149,6 +149,14 @@ instance SerialNoObjectL Cluster where instance TagsObjectL Cluster where tagsL = clusterTagsL +$(makeCustomLenses ''MaintenanceData) + +instance TimeStampObjectL MaintenanceData where + mTimeL = maintMtimeL + +instance SerialNoObjectL MaintenanceData where + serialL = maintSerialL + $(makeCustomLenses ''ConfigData) instance SerialNoObjectL ConfigData where diff --git a/src/Ganeti/Objects/Maintenance.hs b/src/Ganeti/Objects/Maintenance.hs new file mode 100644 index 0000000..8587f81 --- /dev/null +++ b/src/Ganeti/Objects/Maintenance.hs @@ -0,0 +1,59 @@ +{-# LANGUAGE TemplateHaskell #-} + +{-| Implementation of the Ganeti configuration for the maintenance daemon. + +-} + +{- + +Copyright (C) 2015 Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +-} + +module Ganeti.Objects.Maintenance + ( MaintenanceData(..) + ) where + +import qualified Ganeti.Constants as C +import Ganeti.THH +import Ganeti.THH.Field +import Ganeti.Types + +$(buildObject "MaintenanceData" "maint" $ + [ defaultField [| C.maintdDefaultRoundDelay |] + $ simpleField "roundDelay" [t| Int |] + , defaultField [| [] |] $ simpleField "jobs" [t| [ JobId ] |] + ] + ++ timeStampFields + ++ serialFields) + +instance SerialNoObject MaintenanceData where + serialOf = maintSerial + +instance TimeStampObject MaintenanceData where + cTimeOf = maintCtime + mTimeOf = maintMtime diff --git a/test/hs/Test/Ganeti/Objects.hs b/test/hs/Test/Ganeti/Objects.hs index 319e7ee..4b116e1 100644 --- a/test/hs/Test/Ganeti/Objects.hs +++ b/test/hs/Test/Ganeti/Objects.hs @@ -375,6 +375,13 @@ instance Arbitrary FilterRule where <*> arbitrary <*> genUUID +instance Arbitrary MaintenanceData where + arbitrary = MaintenanceData <$> (fromPositive <$> arbitrary) + <*> arbitrary + <*> arbitrary + <*> arbitrary + <*> arbitrary + -- | Generates a network instance with minimum netmasks of /24. Generating -- bigger networks slows down the tests, because long bit strings are generated -- for the reservations. @@ -431,6 +438,7 @@ genEmptyCluster ncount = do networks = GenericContainer Map.empty disks = GenericContainer Map.empty filters = GenericContainer Map.empty + maintenance <- arbitrary let contgroups = GenericContainer $ Map.singleton guuid grp serial <- arbitrary -- timestamp fields @@ -438,7 +446,7 @@ genEmptyCluster ncount = do mtime <- arbitrary cluster <- resize 8 arbitrary let c = ConfigData version cluster contnodes contgroups continsts networks - disks filters ctime mtime serial + disks filters ctime maintenance mtime serial return c -- | FIXME: make an even simpler base version of creating a cluster. diff --git a/test/py/cfgupgrade_unittest.py b/test/py/cfgupgrade_unittest.py index eb4a396..a436351 100755 --- a/test/py/cfgupgrade_unittest.py +++ b/test/py/cfgupgrade_unittest.py @@ -79,6 +79,7 @@ def GetMinimalConfig(): "disks": {}, "networks": {}, "filters": {}, + "maintenance": {}, "nodegroups": {}, "nodes": { "node1-uuid": { -- 2.4.3.573.g4eafbef
