Statistics
| Branch: | Tag: | Revision:

root / src / Ganeti / Locking / Locks.hs @ 5714a925

History | View | Annotate | Download (7.6 kB)

1
{-# LANGUAGE ViewPatterns, FlexibleContexts #-}
2

    
3
{-| Ganeti lock structure
4

    
5
-}
6

    
7
{-
8

    
9
Copyright (C) 2014 Google Inc.
10

    
11
This program is free software; you can redistribute it and/or modify
12
it under the terms of the GNU General Public License as published by
13
the Free Software Foundation; either version 2 of the License, or
14
(at your option) any later version.
15

    
16
This program is distributed in the hope that it will be useful, but
17
WITHOUT ANY WARRANTY; without even the implied warranty of
18
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19
General Public License for more details.
20

    
21
You should have received a copy of the GNU General Public License
22
along with this program; if not, write to the Free Software
23
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24
02110-1301, USA.
25

    
26
-}
27

    
28
module Ganeti.Locking.Locks
29
  ( GanetiLocks(..)
30
  , GanetiLockAllocation
31
  , loadLockAllocation
32
  , writeLocksAsyncTask
33
  , LockLevel(..)
34
  , lockLevel
35
  ) where
36

    
37
import Control.Monad ((>=>))
38
import Control.Monad.Base (MonadBase, liftBase)
39
import Control.Monad.Error (MonadError, catchError)
40
import Data.List (stripPrefix)
41
import qualified Text.JSON as J
42

    
43

    
44
import Ganeti.BasicTypes
45
import Ganeti.Errors (ResultG, GanetiException)
46
import Ganeti.JSON (readEitherString, fromJResultE)
47
import Ganeti.Locking.Allocation
48
import Ganeti.Locking.Types
49
import Ganeti.Logging.Lifted (MonadLog, logDebug, logEmergency)
50
import Ganeti.Types
51
import Ganeti.Utils.Atomic
52
import Ganeti.Utils.AsyncWorker
53

    
54
-- | The type of Locks available in Ganeti. The order of this type
55
-- is the lock oder.
56
data GanetiLocks = BGL
57
                 | ClusterLockSet
58
                 | InstanceLockSet
59
                 | Instance String
60
                 | NodeAllocLockSet
61
                 | NAL
62
                 | NodeGroupLockSet
63
                 | NodeGroup String
64
                 | NodeLockSet
65
                 | Node String
66
                 | NodeResLockSet
67
                 | NodeRes String
68
                 | NetworkLockSet
69
                 | Network String
70
                 deriving (Ord, Eq, Show)
71

    
72
-- | Provide the String representation of a lock
73
lockName :: GanetiLocks -> String
74
lockName BGL = "cluster/BGL"
75
lockName ClusterLockSet = "cluster/[lockset]"
76
lockName InstanceLockSet = "instance/[lockset]"
77
lockName NodeAllocLockSet = "node-alloc/[lockset]"
78
lockName NAL = "node-alloc/NAL"
79
lockName (Instance uuid) = "instance/" ++ uuid
80
lockName NodeGroupLockSet = "nodegroup/[lockset]"
81
lockName (NodeGroup uuid) = "nodegroup/" ++ uuid
82
lockName NodeLockSet = "node/[lockset]"
83
lockName (Node uuid) = "node/" ++ uuid
84
lockName NodeResLockSet = "node-res/[lockset]"
85
lockName (NodeRes uuid) = "node-res/" ++ uuid
86
lockName NetworkLockSet = "network/[lockset]"
87
lockName (Network uuid) = "network/" ++ uuid
88

    
89
-- | Obtain a lock from its name.
90
lockFromName :: String -> J.Result GanetiLocks
91
lockFromName "cluster/BGL" = return BGL
92
lockFromName "cluster/[lockset]" = return ClusterLockSet
93
lockFromName "instance/[lockset]" = return InstanceLockSet
94
lockFromName (stripPrefix "instance/" -> Just uuid) = return $ Instance uuid
95
lockFromName "nodegroup/[lockset]" = return NodeGroupLockSet
96
lockFromName (stripPrefix "nodegroup/" -> Just uuid) = return $ NodeGroup uuid
97
lockFromName "node-alloc/[lockset]" = return NodeAllocLockSet
98
lockFromName "node-alloc/NAL" = return NAL
99
lockFromName "node-res/[lockset]" = return NodeResLockSet
100
lockFromName (stripPrefix "node-res/" -> Just uuid) = return $ NodeRes uuid
101
lockFromName "node/[lockset]" = return NodeLockSet
102
lockFromName (stripPrefix "node/" -> Just uuid) = return $ Node uuid
103
lockFromName "network/[lockset]" = return NetworkLockSet
104
lockFromName (stripPrefix "network/" -> Just uuid) = return $ Network uuid
105
lockFromName n = fail $ "Unknown lock name '" ++ n ++ "'"
106

    
107
instance J.JSON GanetiLocks where
108
  showJSON = J.JSString . J.toJSString . lockName
109
  readJSON = readEitherString >=> lockFromName
110

    
111
-- | The levels, the locks belong to.
112
data LockLevel = LevelCluster
113
               | LevelInstance
114
               | LevelNodeAlloc
115
               | LevelNodeGroup
116
               | LevelNode
117
               | LevelNodeRes
118
               | LevelNetwork
119
               deriving (Eq, Show, Enum)
120

    
121
-- | Provide the names of the lock levels.
122
lockLevelName :: LockLevel -> String
123
lockLevelName LevelCluster = "cluster"
124
lockLevelName LevelInstance = "instance"
125
lockLevelName LevelNodeAlloc = "node-alloc"
126
lockLevelName LevelNodeGroup = "node-group"
127
lockLevelName LevelNode = "node"
128
lockLevelName LevelNodeRes = "node-res"
129
lockLevelName LevelNetwork = "network"
130

    
131
-- | Obtain a lock level from its name/
132
lockLevelFromName :: String -> J.Result LockLevel
133
lockLevelFromName "cluster" = return LevelCluster
134
lockLevelFromName "instance" = return LevelInstance
135
lockLevelFromName "node-alloc" = return LevelNodeAlloc
136
lockLevelFromName "node-group" = return LevelNodeGroup
137
lockLevelFromName "node" = return LevelNode
138
lockLevelFromName "node-res" = return LevelNodeRes
139
lockLevelFromName "network" = return LevelNetwork
140
lockLevelFromName n = fail $ "Unknown lock-level name '" ++ n ++ "'"
141

    
142
instance J.JSON LockLevel where
143
  showJSON = J.JSString . J.toJSString . lockLevelName
144
  readJSON = readEitherString >=> lockLevelFromName
145

    
146
-- | For a lock, provide its level.
147
lockLevel :: GanetiLocks -> LockLevel
148
lockLevel BGL = LevelCluster
149
lockLevel ClusterLockSet = LevelCluster
150
lockLevel InstanceLockSet = LevelInstance
151
lockLevel NodeAllocLockSet = LevelNodeAlloc
152
lockLevel NAL = LevelNodeAlloc
153
lockLevel (Instance _) = LevelInstance
154
lockLevel NodeGroupLockSet = LevelNodeGroup
155
lockLevel (NodeGroup _) = LevelNodeGroup
156
lockLevel NodeLockSet = LevelNode
157
lockLevel (Node _) = LevelNode
158
lockLevel NodeResLockSet = LevelNodeRes
159
lockLevel (NodeRes _) = LevelNodeRes
160
lockLevel NetworkLockSet = LevelNetwork
161
lockLevel (Network _) = LevelNetwork
162

    
163
instance Lock GanetiLocks where
164
  lockImplications BGL = []
165
  lockImplications (Instance _) = [InstanceLockSet, BGL]
166
  lockImplications (NodeGroup _) = [NodeGroupLockSet, BGL]
167
  lockImplications NAL = [NodeAllocLockSet, BGL]
168
  lockImplications (NodeRes _) = [NodeResLockSet, BGL]
169
  lockImplications (Node _) = [NodeLockSet, BGL]
170
  lockImplications (Network _) = [NetworkLockSet, BGL]
171
  lockImplications _ = [BGL]
172

    
173
-- | The type of lock Allocations in Ganeti. In Ganeti, the owner of
174
-- locks are jobs.
175
type GanetiLockAllocation = LockAllocation GanetiLocks (JobId, FilePath)
176

    
177
-- | Load a lock allocation from disk.
178
loadLockAllocation :: FilePath -> ResultG GanetiLockAllocation
179
loadLockAllocation =
180
  liftIO . readFile
181
  >=> fromJResultE "parsing lock allocation" . J.decodeStrict
182

    
183
-- | Write lock allocation to disk, overwriting any previously lock
184
-- allocation stored there.
185
writeLocks :: (MonadBase IO m, MonadError GanetiException m, MonadLog m)
186
           => FilePath -> GanetiLockAllocation -> m ()
187
writeLocks fpath lockAlloc = do
188
  logDebug "Async. lock allocation writer: Starting write"
189
  toErrorBase . liftIO . atomicWriteFile fpath $ J.encode lockAlloc
190
  logDebug "Async. lock allocation writer: written"
191

    
192
-- | Construct an asynchronous worker whose action is to save the
193
-- current state of the lock allocation.
194
-- The worker's action reads the lock allocation using the given @IO@
195
-- action. Any inbetween changes to the file are tacitly ignored.
196
writeLocksAsyncTask :: FilePath -- ^ Path to the lock file
197
                    -> IO GanetiLockAllocation -- ^ An action to read the
198
                                               -- current lock allocation
199
                    -> ResultG (AsyncWorker ())
200
writeLocksAsyncTask fpath lockAllocAction = mkAsyncWorker $
201
  catchError (do
202
    locks <- liftBase lockAllocAction
203
    writeLocks fpath locks
204
  ) (logEmergency . (++) "Can't write lock allocation status: " . show)