These functions now make RPCs to WConfd
instead of locking the config.
Signed-off-by: BSRK Aditya <[email protected]>
---
lib/config/__init__.py | 26 +++---
src/Ganeti/WConfd/ConfigModifications.hs | 137 ++++++++++++++++++++++++++++--
2 files changed, 140 insertions(+), 23 deletions(-)
diff --git a/lib/config/__init__.py b/lib/config/__init__.py
index ec1ce51..220f50d 100644
--- a/lib/config/__init__.py
+++ b/lib/config/__init__.py
@@ -376,25 +376,21 @@ class ConfigWriter(object):
instance.serial_no += 1
instance.mtime = time.time()
- @ConfigSync()
def AddInstanceDisk(self, inst_uuid, disk, idx=None, replace=False):
- """Add a disk to the config and attach it to instance.
-
- This is a simple wrapper over L{_UnlockedAddDisk} and
- L{_UnlockedAttachInstanceDisk}.
+ """Add a disk to the config and attach it to instance."""
+ if not isinstance(disk, objects.Disk):
+ raise errors.ProgrammerError("Invalid type passed to AddInstanceDisk")
- """
- self._UnlockedAddDisk(disk, replace=replace)
- self._UnlockedAttachInstanceDisk(inst_uuid, disk.uuid, idx)
+ disk.UpgradeConfig()
+ utils.SimpleRetry(True, self._wconfd.AddInstanceDisk, 0.1, 30,
+ args=[inst_uuid, disk.ToDict(), idx, replace])
+ self.OutDate()
- @ConfigSync()
def AttachInstanceDisk(self, inst_uuid, disk_uuid, idx=None):
- """Attach an existing disk to an instance.
-
- This is a simple wrapper over L{_UnlockedAttachInstanceDisk}.
-
- """
- self._UnlockedAttachInstanceDisk(inst_uuid, disk_uuid, idx)
+ """Attach an existing disk to an instance."""
+ utils.SimpleRetry(True, self._wconfd.AttachInstanceDisk, 0.1, 30,
+ args=[inst_uuid, disk_uuid, idx])
+ self.OutDate()
def _UnlockedDetachInstanceDisk(self, inst_uuid, disk_uuid):
"""Detach a disk from an instance.
diff --git a/src/Ganeti/WConfd/ConfigModifications.hs
b/src/Ganeti/WConfd/ConfigModifications.hs
index c51b4b1..8a69599 100644
--- a/src/Ganeti/WConfd/ConfigModifications.hs
+++ b/src/Ganeti/WConfd/ConfigModifications.hs
@@ -39,28 +39,43 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
module Ganeti.WConfd.ConfigModifications where
-import Control.Lens.Setter ((.~))
+import Control.Lens.Setter ((.~), (%~))
import Control.Lens.Traversal (mapMOf)
-import Control.Monad (unless)
+import Control.Monad (unless, when, forM_)
import Control.Monad.IO.Class (liftIO)
-import Data.Maybe (isJust, maybeToList)
+import Data.Maybe (isJust, maybeToList, fromMaybe)
import Language.Haskell.TH (Name)
-import System.Time (getClockTime)
+import System.Time (getClockTime, ClockTime)
import Text.Printf (printf)
import qualified Data.Map as M
import qualified Data.Set as S
-import Ganeti.BasicTypes (GenericResult(..), toError)
+import Ganeti.BasicTypes (GenericResult(..), genericResult, toError)
import Ganeti.Errors (GanetiException(..))
-import Ganeti.JSON (GenericContainer(..), alterContainerL)
+import Ganeti.JSON (Container, GenericContainer(..), alterContainerL
+ , lookupContainer, MaybeForJSON(..))
import Ganeti.Locking.Locks (ClientId, ciIdentifier)
-import Ganeti.Logging.Lifted (logDebug)
+import Ganeti.Logging.Lifted (logDebug, logInfo)
import Ganeti.Objects
import Ganeti.Objects.Lens
import Ganeti.WConfd.ConfigState (ConfigState, csConfigData, csConfigDataL)
import Ganeti.WConfd.Monad (WConfdMonad, modifyConfigWithLock)
import qualified Ganeti.WConfd.TempRes as T
+type DiskUUID = String
+type InstanceUUID = String
+
+-- * accessor functions
+
+getInstanceByUUID :: ConfigState
+ -> InstanceUUID
+ -> GenericResult GanetiException Instance
+getInstanceByUUID cs uuid = lookupContainer
+ (Bad . ConfigurationError $
+ printf "Could not find instance with UUID %s" uuid)
+ uuid
+ (configInstances . csConfigData $ cs)
+
-- * getters
-- | Gets all logical volumes in the cluster
@@ -89,7 +104,7 @@ getAllIDs cs =
instKeys = keysFromC . configInstances . csConfigData $ cs
nodeKeys = keysFromC . configNodes . csConfigData $ cs
-
+
instValues = map uuidOf . valuesFromC
. configInstances . csConfigData $ cs
nodeValues = map uuidOf . valuesFromC . configNodes . csConfigData $ cs
@@ -158,6 +173,79 @@ addInstanceChecks inst replace cs = do
"Cannot replace %s: UUID %s not present"
(show $ instName inst) (instUuid inst)
+addDiskChecks :: Disk
+ -> Bool
+ -> ConfigState
+ -> GenericResult GanetiException ()
+addDiskChecks disk replace cs =
+ if replace
+ then do
+ let check = checkUUIDpresent cs disk
+ unless check . Bad . ConfigurationError $ printf
+ "Cannot add %s: UUID %s already in use"
+ (show $ diskName disk) (diskUuid disk)
+ else do
+ let check = checkUniqueUUID cs disk
+ unless check . Bad . ConfigurationError $ printf
+ "Cannot replace %s: UUID %s not present"
+ (show $ diskName disk) (diskUuid disk)
+
+attachInstanceDiskChecks :: InstanceUUID
+ -> DiskUUID
+ -> MaybeForJSON Int
+ -> ConfigState
+ -> GenericResult GanetiException ()
+attachInstanceDiskChecks uuidInst uuidDisk idx' cs = do
+ let diskPresent = elem uuidDisk . map diskUuid . M.elems
+ . fromContainer . configDisks . csConfigData $ cs
+ unless diskPresent . Bad . ConfigurationError $ printf
+ "Disk %s doesn't exist" uuidDisk
+
+ inst <- getInstanceByUUID cs uuidInst
+ let numDisks = length $ instDisks inst
+ idx = fromMaybe numDisks (unMaybeForJSON idx')
+
+ when (idx < 0) . Bad . GenericError $
+ "Not accepting negative indices other than -1"
+ when (idx > numDisks) . Bad . GenericError $ printf
+ "Got disk index %d, but there are only %d" idx numDisks
+
+ let insts = M.elems . fromContainer . configInstances . csConfigData $ cs
+ forM_ insts (\inst' -> when (uuidDisk `elem` instDisks inst') . Bad
+ . ReservationError $ printf "Disk %s already attached to instance %s"
+ uuidDisk (show $ instName inst))
+
+-- * Pure config modifications functions
+
+attachInstanceDisk' :: InstanceUUID
+ -> DiskUUID
+ -> MaybeForJSON Int
+ -> ClockTime
+ -> ConfigState
+ -> ConfigState
+attachInstanceDisk' iUuid dUuid idx' ct cs =
+ let inst = genericResult (error "impossible") id (getInstanceByUUID cs iUuid)
+ numDisks = length $ instDisks inst
+ idx = fromMaybe numDisks (unMaybeForJSON idx')
+
+ insert = instDisksL %~ (\ds -> take idx ds ++ [dUuid] ++ drop idx ds)
+ incr = instSerialL %~ (+ 1)
+ time = instMtimeL .~ ct
+
+ inst' = time . incr . insert $ inst
+ disks = updateIvNames idx inst' (configDisks . csConfigData $ cs)
+
+ ri = csConfigDataL . configInstancesL
+ . alterContainerL iUuid .~ Just inst'
+ rds = csConfigDataL . configDisksL .~ disks
+ in rds . ri $ cs
+ where updateIvNames :: Int -> Instance -> Container Disk -> Container Disk
+ updateIvNames idx inst (GenericContainer m) =
+ let dUuids = drop idx (instDisks inst)
+ upgradeIv m' (idx'', dUuid') =
+ M.adjust (diskIvNameL .~ "disk/" ++ show idx'') dUuid' m'
+ in GenericContainer $ foldl upgradeIv m (zip [idx..] dUuids)
+
-- * RPCs
-- | Add a new instance to the configuration, release DRBD minors,
@@ -183,8 +271,41 @@ addInstance inst cid replace = do
logDebug $ "AddInstance: result of config modification is " ++ show r
return $ isJust r
+addInstanceDisk :: InstanceUUID
+ -> Disk
+ -> MaybeForJSON Int
+ -> Bool
+ -> WConfdMonad Bool
+addInstanceDisk iUuid disk idx replace = do
+ logInfo $ printf "Adding disk %s to configuration" (diskUuid disk)
+ ct <- liftIO getClockTime
+ let addD = csConfigDataL . configDisksL . alterContainerL (uuidOf disk)
+ .~ Just disk
+ incrSerialNo = csConfigDataL . configSerialL %~ (+1)
+ r <- modifyConfigWithLock (\_ cs -> do
+ toError $ addDiskChecks disk replace cs
+ let cs' = incrSerialNo . addD $ cs
+ toError $ attachInstanceDiskChecks iUuid (diskUuid disk) idx cs'
+ return $ attachInstanceDisk' iUuid (diskUuid disk) idx ct cs')
+ . T.releaseDRBDMinors $ uuidOf disk
+ return $ isJust r
+
+attachInstanceDisk :: InstanceUUID
+ -> DiskUUID
+ -> MaybeForJSON Int
+ -> WConfdMonad Bool
+attachInstanceDisk iUuid dUuid idx = do
+ ct <- liftIO getClockTime
+ r <- modifyConfigWithLock (\_ cs -> do
+ toError $ attachInstanceDiskChecks iUuid dUuid idx cs
+ return $ attachInstanceDisk' iUuid dUuid idx ct cs)
+ (return ())
+ return $ isJust r
+
-- * The list of functions exported to RPC.
exportedFunctions :: [Name]
exportedFunctions = [ 'addInstance
+ , 'addInstanceDisk
+ , 'attachInstanceDisk
]
--
1.7.10.4