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