AddInstance relies upon _ConfigData() for
some validations. However, _ConfigData() cannot
be relied upon without @ConfigSync. This patch
ports these checks to the wconfd daemon.

Signed-off-by: BSRK Aditya <[email protected]>
---
 lib/config/__init__.py                   |   17 +---
 src/Ganeti/WConfd/ConfigModifications.hs |  130 +++++++++++++++++++++++++++---
 2 files changed, 121 insertions(+), 26 deletions(-)

diff --git a/lib/config/__init__.py b/lib/config/__init__.py
index e00573a..ec1ce51 100644
--- a/lib/config/__init__.py
+++ b/lib/config/__init__.py
@@ -1772,23 +1772,12 @@ class ConfigWriter(object):
     if not isinstance(instance, objects.Instance):
       raise errors.ProgrammerError("Invalid type passed to AddInstance")
 
-    all_macs = self._AllMACs()
-    for nic in instance.nics:
-      if nic.mac in all_macs:
-        raise errors.ConfigurationError("Cannot add instance %s:"
-                                        " MAC address '%s' already in use." %
-                                        (instance.name, nic.mac))
-
-    if replace:
-      self._CheckUUIDpresent(instance)
-    else:
-      self._CheckUniqueUUID(instance, include_temporary=False)
-
     instance.serial_no = 1
-    instance.ctime = instance.mtime = time.time()
 
     utils.SimpleRetry(True, self._wconfd.AddInstance, 0.1, 30,
-                      args=[instance.ToDict(), self._GetWConfdContext()])
+                      args=[instance.ToDict(),
+                            self._GetWConfdContext(),
+                            replace])
     self.OutDate()
 
   def _EnsureUUID(self, item, ec_id):
diff --git a/src/Ganeti/WConfd/ConfigModifications.hs 
b/src/Ganeti/WConfd/ConfigModifications.hs
index aa11b2a..e318eb4 100644
--- a/src/Ganeti/WConfd/ConfigModifications.hs
+++ b/src/Ganeti/WConfd/ConfigModifications.hs
@@ -41,37 +41,143 @@ module Ganeti.WConfd.ConfigModifications where
 
 import Control.Lens.Setter ((.~))
 import Control.Lens.Traversal (mapMOf)
-import Data.Maybe (isJust)
+import Control.Monad (unless)
+import Control.Monad.IO.Class (liftIO)
+import Data.List (nub, intersect)
+import Data.Maybe (isJust, maybeToList)
 import Language.Haskell.TH (Name)
+import System.Time (getClockTime)
+import Text.Printf (printf)
+import qualified Data.Map as M
 
-import Ganeti.JSON (alterContainerL)
+import Ganeti.BasicTypes (GenericResult(..), toError)
+import Ganeti.Errors (GanetiException(..))
+import Ganeti.JSON (GenericContainer(..), alterContainerL)
 import Ganeti.Locking.Locks (ClientId, ciIdentifier)
 import Ganeti.Logging.Lifted (logDebug)
 import Ganeti.Objects
 import Ganeti.Objects.Lens
-import Ganeti.WConfd.ConfigState (csConfigDataL)
+import Ganeti.WConfd.ConfigState (ConfigState, csConfigData, csConfigDataL)
 import Ganeti.WConfd.Monad (WConfdMonad, modifyConfigWithLock)
 import qualified Ganeti.WConfd.TempRes as T
 
+-- * getters
+
+-- | Gets all logical volumes in the cluster
+getAllLVs :: ConfigState -> [String]
+getAllLVs = concatMap getLVsOfDisk . M.elems
+          . fromContainer . configDisks  . csConfigData
+  where convert (LogicalVolume lvG lvV) = lvG ++ "/" ++ lvV
+        getDiskLV :: Disk -> Maybe String
+        getDiskLV disk = case diskLogicalId disk of
+          Just (LIDPlain lv) -> Just (convert lv)
+          _ -> Nothing
+        getLVsOfDisk :: Disk -> [String]
+        getLVsOfDisk disk = maybeToList (getDiskLV disk)
+                          ++ concatMap getLVsOfDisk (diskChildren disk)
+
+-- | Gets the ids of nodes, instances, node groups,
+--   networks, disks, nics, and the custer itself.
+getAllIDs :: ConfigState -> [String]
+getAllIDs cs =
+  let lvs = getAllLVs cs
+      keysFromC :: GenericContainer a b -> [a]
+      keysFromC = M.keys . fromContainer
+
+      valuesFromC :: GenericContainer a b -> [b]
+      valuesFromC = M.elems . fromContainer
+
+      instKeys = keysFromC . configInstances . csConfigData $ cs
+      nodeKeys = keysFromC . configNodes . csConfigData $ cs
+      
+      instValues = map uuidOf . valuesFromC
+                 . configInstances . csConfigData $ cs
+      nodeValues = map uuidOf . valuesFromC . configNodes . csConfigData $ cs
+      nodeGroupValues = map uuidOf . valuesFromC
+                      . configNodegroups . csConfigData $ cs
+      networkValues = map uuidOf . valuesFromC
+                    . configNetworks . csConfigData $ cs
+      disksValues = map uuidOf . valuesFromC . configDisks . csConfigData $ cs
+
+      nics = map nicUuid . concatMap instNics
+           . valuesFromC . configInstances . csConfigData $ cs
+
+      cluster = uuidOf . configCluster . csConfigData $ cs
+  in nub $ lvs ++ instKeys ++ nodeKeys ++ instValues ++ nodeValues
+         ++ nodeGroupValues ++ networkValues ++ disksValues ++ nics ++ 
[cluster]
+
+getAllMACs :: ConfigState -> [String]
+getAllMACs = map nicMac . concatMap instNics . M.elems
+           . fromContainer . configInstances . csConfigData
+
+-- * UUID config checks
+
+-- | Checks if the config has the given UUID
+checkUUIDpresent :: UuidObject a
+                 => ConfigState
+                 -> a
+                 -> Bool
+checkUUIDpresent cs a = uuidOf a `elem` getAllIDs cs
+
+-- | Checks if the given UUID is new (i.e., no in the config)
+checkUniqueUUID :: UuidObject a
+                => ConfigState
+                -> a
+                -> Bool
+checkUniqueUUID cs a = not $ checkUUIDpresent cs a
+
+-- * RPC checks
+
+-- | Verifications done before adding an instance.
+-- Currently confirms that the instance's macs are not
+-- in use, and that the instance's UUID being
+-- present (or not present) in the config based on
+-- weather the instance is being replaced (or not).
+--
+-- TODO: add more verifications to this call;
+-- the client should have a lock on the name of the instance.
+addInstanceChecks :: Instance
+                  -> Bool
+                  -> ConfigState
+                  -> GenericResult GanetiException ()
+addInstanceChecks inst replace cs = do
+  let macsInUse = map nicMac (instNics inst) `intersect` getAllMACs cs
+  unless (null macsInUse) . Bad . ConfigurationError $ printf
+    "Cannot add instance %s; MAC addresses %s already in use"
+    (show $ instName inst) (show macsInUse)
+  if replace
+    then do
+      let check = checkUUIDpresent cs inst
+      unless check . Bad . ConfigurationError $ printf
+             "Cannot add %s: UUID %s already in use"
+             (show $ instName inst) (instUuid inst)
+    else do
+      let check = checkUniqueUUID cs inst
+      unless check . Bad . ConfigurationError $ printf
+             "Cannot replace %s: UUID %s not present"
+             (show $ instName inst) (instUuid inst)
+
+-- * RPCs
 
 -- | Add a new instance to the configuration, release DRBD minors,
 -- and commit temporary IPs, all while temporarily holding the config
 -- lock. Return True upon success and False if the config lock was not
 -- available and the client should retry.
---
--- TODO: add verifications to this call; the client should have a lock
--- on the name of the instance, and no instance with the given uuid should
--- exist.
-addInstance :: Instance -> ClientId -> WConfdMonad Bool
-addInstance inst cid = do
+addInstance :: Instance -> ClientId -> Bool -> WConfdMonad Bool
+addInstance inst cid replace = do
+  ct <- liftIO getClockTime
   logDebug $ "AddInstance: client " ++ show (ciIdentifier cid)
              ++ " adding instance " ++ uuidOf inst
              ++ " with name " ++ show (instName inst)
-  let addInst = csConfigDataL . configInstancesL . alterContainerL (uuidOf 
inst)
-                  .~ Just inst
+  let setCtime = instCtimeL .~ ct
+      setMtime = instMtimeL .~ ct
+      addInst i = csConfigDataL . configInstancesL . alterContainerL (uuidOf i)
+                  .~ Just i
       commitRes tr = mapMOf csConfigDataL $ T.commitReservedIps cid tr
   r <- modifyConfigWithLock
-         (\tr cs -> commitRes tr $ addInst  cs)
+         (\tr cs -> do
+           toError $ addInstanceChecks inst replace cs
+           commitRes tr $ addInst (setMtime . setCtime $ inst) cs)
          . T.releaseDRBDMinors $ uuidOf inst
   logDebug $ "AddInstance: result of config modification is " ++ show r
   return $ isJust r
-- 
1.7.10.4

Reply via email to