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

Reply via email to