root / lib / cmdlib / base.py @ 6ea039ef
History | View | Annotate | Download (21.5 kB)
1 | 1a732a74 | Thomas Thrainer | #
|
---|---|---|---|
2 | 1a732a74 | Thomas Thrainer | #
|
3 | 1a732a74 | Thomas Thrainer | |
4 | 1a732a74 | Thomas Thrainer | # Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc.
|
5 | 1a732a74 | Thomas Thrainer | #
|
6 | 1a732a74 | Thomas Thrainer | # This program is free software; you can redistribute it and/or modify
|
7 | 1a732a74 | Thomas Thrainer | # it under the terms of the GNU General Public License as published by
|
8 | 1a732a74 | Thomas Thrainer | # the Free Software Foundation; either version 2 of the License, or
|
9 | 1a732a74 | Thomas Thrainer | # (at your option) any later version.
|
10 | 1a732a74 | Thomas Thrainer | #
|
11 | 1a732a74 | Thomas Thrainer | # This program is distributed in the hope that it will be useful, but
|
12 | 1a732a74 | Thomas Thrainer | # WITHOUT ANY WARRANTY; without even the implied warranty of
|
13 | 1a732a74 | Thomas Thrainer | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14 | 1a732a74 | Thomas Thrainer | # General Public License for more details.
|
15 | 1a732a74 | Thomas Thrainer | #
|
16 | 1a732a74 | Thomas Thrainer | # You should have received a copy of the GNU General Public License
|
17 | 1a732a74 | Thomas Thrainer | # along with this program; if not, write to the Free Software
|
18 | 1a732a74 | Thomas Thrainer | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
19 | 1a732a74 | Thomas Thrainer | # 02110-1301, USA.
|
20 | 1a732a74 | Thomas Thrainer | |
21 | 1a732a74 | Thomas Thrainer | |
22 | 1a732a74 | Thomas Thrainer | """Base classes and functions for cmdlib."""
|
23 | 1a732a74 | Thomas Thrainer | |
24 | 1a732a74 | Thomas Thrainer | import logging |
25 | 1a732a74 | Thomas Thrainer | |
26 | 1a732a74 | Thomas Thrainer | from ganeti import errors |
27 | 1a732a74 | Thomas Thrainer | from ganeti import constants |
28 | 1a732a74 | Thomas Thrainer | from ganeti import locking |
29 | 1a732a74 | Thomas Thrainer | from ganeti import query |
30 | 1a732a74 | Thomas Thrainer | from ganeti import utils |
31 | da4a52a3 | Thomas Thrainer | from ganeti.cmdlib.common import ExpandInstanceUuidAndName |
32 | 1a732a74 | Thomas Thrainer | |
33 | 1a732a74 | Thomas Thrainer | |
34 | 1a732a74 | Thomas Thrainer | class ResultWithJobs: |
35 | 1a732a74 | Thomas Thrainer | """Data container for LU results with jobs.
|
36 | 1a732a74 | Thomas Thrainer |
|
37 | 1a732a74 | Thomas Thrainer | Instances of this class returned from L{LogicalUnit.Exec} will be recognized
|
38 | 1a732a74 | Thomas Thrainer | by L{mcpu._ProcessResult}. The latter will then submit the jobs
|
39 | 1a732a74 | Thomas Thrainer | contained in the C{jobs} attribute and include the job IDs in the opcode
|
40 | 1a732a74 | Thomas Thrainer | result.
|
41 | 1a732a74 | Thomas Thrainer |
|
42 | 1a732a74 | Thomas Thrainer | """
|
43 | 1a732a74 | Thomas Thrainer | def __init__(self, jobs, **kwargs): |
44 | 1a732a74 | Thomas Thrainer | """Initializes this class.
|
45 | 1a732a74 | Thomas Thrainer |
|
46 | 1a732a74 | Thomas Thrainer | Additional return values can be specified as keyword arguments.
|
47 | 1a732a74 | Thomas Thrainer |
|
48 | 1a732a74 | Thomas Thrainer | @type jobs: list of lists of L{opcode.OpCode}
|
49 | 1a732a74 | Thomas Thrainer | @param jobs: A list of lists of opcode objects
|
50 | 1a732a74 | Thomas Thrainer |
|
51 | 1a732a74 | Thomas Thrainer | """
|
52 | 1a732a74 | Thomas Thrainer | self.jobs = jobs
|
53 | 1a732a74 | Thomas Thrainer | self.other = kwargs
|
54 | 1a732a74 | Thomas Thrainer | |
55 | 1a732a74 | Thomas Thrainer | |
56 | 87ed6b79 | Klaus Aehlig | class LUWConfdClient(object): |
57 | 87ed6b79 | Klaus Aehlig | """Wrapper class for wconfd client calls from LUs.
|
58 | 87ed6b79 | Klaus Aehlig |
|
59 | 87ed6b79 | Klaus Aehlig | Correctly updates the cache of the LU's owned locks
|
60 | 87ed6b79 | Klaus Aehlig | when leaving. Also transparently adds the context
|
61 | 87ed6b79 | Klaus Aehlig | for resource requests.
|
62 | 87ed6b79 | Klaus Aehlig |
|
63 | 87ed6b79 | Klaus Aehlig | """
|
64 | 87ed6b79 | Klaus Aehlig | def __init__(self, lu): |
65 | 87ed6b79 | Klaus Aehlig | self.lu = lu
|
66 | 87ed6b79 | Klaus Aehlig | |
67 | 87ed6b79 | Klaus Aehlig | def TryUpdateLocks(self, req): |
68 | 6ea039ef | Petr Pudlak | self.lu.wconfd.Client().TryUpdateLocks(self.lu.wconfdcontext, req) |
69 | 6ea039ef | Petr Pudlak | self.lu.wconfdlocks = \
|
70 | 6ea039ef | Petr Pudlak | self.lu.wconfd.Client().ListLocks(self.lu.wconfdcontext) |
71 | 87ed6b79 | Klaus Aehlig | |
72 | 87ed6b79 | Klaus Aehlig | def DownGradeLocksLevel(self, level): |
73 | 6ea039ef | Petr Pudlak | self.lu.wconfd.Client().DownGradeLocksLevel(self.lu.wconfdcontext, level) |
74 | 6ea039ef | Petr Pudlak | self.lu.wconfdlocks = \
|
75 | 6ea039ef | Petr Pudlak | self.lu.wconfd.Client().ListLocks(self.lu.wconfdcontext) |
76 | 87ed6b79 | Klaus Aehlig | |
77 | 87ed6b79 | Klaus Aehlig | def FreeLocksLevel(self, level): |
78 | 6ea039ef | Petr Pudlak | self.lu.wconfd.Client().FreeLocksLevel(self.lu.wconfdcontext, level) |
79 | 6ea039ef | Petr Pudlak | self.lu.wconfdlocks = \
|
80 | 6ea039ef | Petr Pudlak | self.lu.wconfd.Client().ListLocks(self.lu.wconfdcontext) |
81 | 87ed6b79 | Klaus Aehlig | |
82 | 87ed6b79 | Klaus Aehlig | |
83 | 1a732a74 | Thomas Thrainer | class LogicalUnit(object): |
84 | 1a732a74 | Thomas Thrainer | """Logical Unit base class.
|
85 | 1a732a74 | Thomas Thrainer |
|
86 | 1a732a74 | Thomas Thrainer | Subclasses must follow these rules:
|
87 | 1a732a74 | Thomas Thrainer | - implement ExpandNames
|
88 | 1a732a74 | Thomas Thrainer | - implement CheckPrereq (except when tasklets are used)
|
89 | 1a732a74 | Thomas Thrainer | - implement Exec (except when tasklets are used)
|
90 | 1a732a74 | Thomas Thrainer | - implement BuildHooksEnv
|
91 | 1a732a74 | Thomas Thrainer | - implement BuildHooksNodes
|
92 | 1a732a74 | Thomas Thrainer | - redefine HPATH and HTYPE
|
93 | 1a732a74 | Thomas Thrainer | - optionally redefine their run requirements:
|
94 | 1a732a74 | Thomas Thrainer | REQ_BGL: the LU needs to hold the Big Ganeti Lock exclusively
|
95 | 1a732a74 | Thomas Thrainer |
|
96 | 1a732a74 | Thomas Thrainer | Note that all commands require root permissions.
|
97 | 1a732a74 | Thomas Thrainer |
|
98 | 1a732a74 | Thomas Thrainer | @ivar dry_run_result: the value (if any) that will be returned to the caller
|
99 | 1a732a74 | Thomas Thrainer | in dry-run mode (signalled by opcode dry_run parameter)
|
100 | 1a732a74 | Thomas Thrainer |
|
101 | 1a732a74 | Thomas Thrainer | """
|
102 | 1a732a74 | Thomas Thrainer | HPATH = None
|
103 | 1a732a74 | Thomas Thrainer | HTYPE = None
|
104 | 1a732a74 | Thomas Thrainer | REQ_BGL = True
|
105 | 1a732a74 | Thomas Thrainer | |
106 | 87ed6b79 | Klaus Aehlig | def __init__(self, processor, op, context, rpc_runner, wconfdcontext, wconfd): |
107 | 1a732a74 | Thomas Thrainer | """Constructor for LogicalUnit.
|
108 | 1a732a74 | Thomas Thrainer |
|
109 | 1a732a74 | Thomas Thrainer | This needs to be overridden in derived classes in order to check op
|
110 | 1a732a74 | Thomas Thrainer | validity.
|
111 | 1a732a74 | Thomas Thrainer |
|
112 | 87ed6b79 | Klaus Aehlig | @type wconfdcontext: (int, string)
|
113 | 87ed6b79 | Klaus Aehlig | @param wconfdcontext: the identity of the logical unit to represent itself
|
114 | 87ed6b79 | Klaus Aehlig | to wconfd when asking for resources; it is given as job id and livelock
|
115 | 87ed6b79 | Klaus Aehlig | file.
|
116 | 87ed6b79 | Klaus Aehlig | @param wconfd: the wconfd class to use; dependency injection to allow
|
117 | 87ed6b79 | Klaus Aehlig | testability.
|
118 | 87ed6b79 | Klaus Aehlig |
|
119 | 1a732a74 | Thomas Thrainer | """
|
120 | 1a732a74 | Thomas Thrainer | self.proc = processor
|
121 | 1a732a74 | Thomas Thrainer | self.op = op
|
122 | 1a732a74 | Thomas Thrainer | self.cfg = context.cfg
|
123 | 87ed6b79 | Klaus Aehlig | self.wconfdlocks = []
|
124 | 87ed6b79 | Klaus Aehlig | self.wconfdcontext = wconfdcontext
|
125 | 1a732a74 | Thomas Thrainer | self.context = context
|
126 | 1a732a74 | Thomas Thrainer | self.rpc = rpc_runner
|
127 | 87ed6b79 | Klaus Aehlig | self.wconfd = wconfd # wconfd module to use, for testing |
128 | 1a732a74 | Thomas Thrainer | |
129 | 1a732a74 | Thomas Thrainer | # Dictionaries used to declare locking needs to mcpu
|
130 | 1a732a74 | Thomas Thrainer | self.needed_locks = None |
131 | 1a732a74 | Thomas Thrainer | self.share_locks = dict.fromkeys(locking.LEVELS, 0) |
132 | 1a732a74 | Thomas Thrainer | self.opportunistic_locks = dict.fromkeys(locking.LEVELS, False) |
133 | 1a732a74 | Thomas Thrainer | |
134 | 1a732a74 | Thomas Thrainer | self.add_locks = {}
|
135 | 1a732a74 | Thomas Thrainer | self.remove_locks = {}
|
136 | 1a732a74 | Thomas Thrainer | |
137 | 1a732a74 | Thomas Thrainer | # Used to force good behavior when calling helper functions
|
138 | 1a732a74 | Thomas Thrainer | self.recalculate_locks = {}
|
139 | 1a732a74 | Thomas Thrainer | |
140 | 1a732a74 | Thomas Thrainer | # logging
|
141 | 1a732a74 | Thomas Thrainer | self.Log = processor.Log # pylint: disable=C0103 |
142 | 1a732a74 | Thomas Thrainer | self.LogWarning = processor.LogWarning # pylint: disable=C0103 |
143 | 1a732a74 | Thomas Thrainer | self.LogInfo = processor.LogInfo # pylint: disable=C0103 |
144 | 1a732a74 | Thomas Thrainer | self.LogStep = processor.LogStep # pylint: disable=C0103 |
145 | 1a732a74 | Thomas Thrainer | # support for dry-run
|
146 | 1a732a74 | Thomas Thrainer | self.dry_run_result = None |
147 | 1a732a74 | Thomas Thrainer | # support for generic debug attribute
|
148 | 1a732a74 | Thomas Thrainer | if (not hasattr(self.op, "debug_level") or |
149 | 1a732a74 | Thomas Thrainer | not isinstance(self.op.debug_level, int)): |
150 | 1a732a74 | Thomas Thrainer | self.op.debug_level = 0 |
151 | 1a732a74 | Thomas Thrainer | |
152 | 1a732a74 | Thomas Thrainer | # Tasklets
|
153 | 1a732a74 | Thomas Thrainer | self.tasklets = None |
154 | 1a732a74 | Thomas Thrainer | |
155 | 1a732a74 | Thomas Thrainer | # Validate opcode parameters and set defaults
|
156 | 1a732a74 | Thomas Thrainer | self.op.Validate(True) |
157 | 1a732a74 | Thomas Thrainer | |
158 | 1a732a74 | Thomas Thrainer | self.CheckArguments()
|
159 | 1a732a74 | Thomas Thrainer | |
160 | 87ed6b79 | Klaus Aehlig | def WConfdClient(self): |
161 | 87ed6b79 | Klaus Aehlig | return LUWConfdClient(self) |
162 | 87ed6b79 | Klaus Aehlig | |
163 | 87ed6b79 | Klaus Aehlig | def owned_locks(self, level): |
164 | 87ed6b79 | Klaus Aehlig | """Return the list of locks owned by the LU at a given level.
|
165 | 87ed6b79 | Klaus Aehlig |
|
166 | 87ed6b79 | Klaus Aehlig | This method assumes that is field wconfdlocks is set correctly
|
167 | 87ed6b79 | Klaus Aehlig | by mcpu.
|
168 | 87ed6b79 | Klaus Aehlig |
|
169 | 87ed6b79 | Klaus Aehlig | """
|
170 | 87ed6b79 | Klaus Aehlig | levelprefix = "%s/" % (locking.LEVEL_NAMES[level],)
|
171 | 87ed6b79 | Klaus Aehlig | locks = set([lock[0][len(levelprefix):] |
172 | 87ed6b79 | Klaus Aehlig | for lock in self.wconfdlocks |
173 | 87ed6b79 | Klaus Aehlig | if lock[0].startswith(levelprefix)]) |
174 | 87ed6b79 | Klaus Aehlig | expand_fns = { |
175 | 87ed6b79 | Klaus Aehlig | locking.LEVEL_CLUSTER: (lambda: [locking.BGL]),
|
176 | 87ed6b79 | Klaus Aehlig | locking.LEVEL_INSTANCE: self.cfg.GetInstanceList,
|
177 | 87ed6b79 | Klaus Aehlig | locking.LEVEL_NODE_ALLOC: (lambda: [locking.NAL]),
|
178 | 87ed6b79 | Klaus Aehlig | locking.LEVEL_NODEGROUP: self.cfg.GetNodeGroupList,
|
179 | 87ed6b79 | Klaus Aehlig | locking.LEVEL_NODE: self.cfg.GetNodeList,
|
180 | 87ed6b79 | Klaus Aehlig | locking.LEVEL_NODE_RES: self.cfg.GetNodeList,
|
181 | 87ed6b79 | Klaus Aehlig | locking.LEVEL_NETWORK: self.cfg.GetNetworkList,
|
182 | 87ed6b79 | Klaus Aehlig | } |
183 | 87ed6b79 | Klaus Aehlig | if locking.LOCKSET_NAME in locks: |
184 | 87ed6b79 | Klaus Aehlig | return expand_fns[level]()
|
185 | 87ed6b79 | Klaus Aehlig | else:
|
186 | 87ed6b79 | Klaus Aehlig | return locks
|
187 | 87ed6b79 | Klaus Aehlig | |
188 | 87ed6b79 | Klaus Aehlig | def release_request(self, level, names): |
189 | 87ed6b79 | Klaus Aehlig | """Return a request to release the specified locks of the given level.
|
190 | 87ed6b79 | Klaus Aehlig |
|
191 | 87ed6b79 | Klaus Aehlig | Correctly break up the group lock to do so.
|
192 | 87ed6b79 | Klaus Aehlig |
|
193 | 87ed6b79 | Klaus Aehlig | """
|
194 | 87ed6b79 | Klaus Aehlig | levelprefix = "%s/" % (locking.LEVEL_NAMES[level],)
|
195 | 87ed6b79 | Klaus Aehlig | release = [[levelprefix + lock, "release"] for lock in names] |
196 | 87ed6b79 | Klaus Aehlig | |
197 | 87ed6b79 | Klaus Aehlig | # if we break up the set-lock, make sure we ask for the rest of it.
|
198 | 87ed6b79 | Klaus Aehlig | setlock = levelprefix + locking.LOCKSET_NAME |
199 | 87ed6b79 | Klaus Aehlig | if [setlock, "exclusive"] in self.wconfdlocks: |
200 | 87ed6b79 | Klaus Aehlig | owned = self.owned_locks(level)
|
201 | 87ed6b79 | Klaus Aehlig | request = [[levelprefix + lock, "exclusive"]
|
202 | 87ed6b79 | Klaus Aehlig | for lock in owned |
203 | 87ed6b79 | Klaus Aehlig | if lock not in names] |
204 | 87ed6b79 | Klaus Aehlig | elif [setlock, "shared"] in self.wconfdlocks: |
205 | 87ed6b79 | Klaus Aehlig | owned = self.owned_locks(level)
|
206 | 87ed6b79 | Klaus Aehlig | request = [[levelprefix + lock, "shared"]
|
207 | 87ed6b79 | Klaus Aehlig | for lock in owned |
208 | 87ed6b79 | Klaus Aehlig | if lock not in names] |
209 | 87ed6b79 | Klaus Aehlig | else:
|
210 | 87ed6b79 | Klaus Aehlig | request = [] |
211 | 87ed6b79 | Klaus Aehlig | |
212 | 87ed6b79 | Klaus Aehlig | return release + [[setlock, "release"]] + request |
213 | 87ed6b79 | Klaus Aehlig | |
214 | 1a732a74 | Thomas Thrainer | def CheckArguments(self): |
215 | 1a732a74 | Thomas Thrainer | """Check syntactic validity for the opcode arguments.
|
216 | 1a732a74 | Thomas Thrainer |
|
217 | 1a732a74 | Thomas Thrainer | This method is for doing a simple syntactic check and ensure
|
218 | 1a732a74 | Thomas Thrainer | validity of opcode parameters, without any cluster-related
|
219 | 1a732a74 | Thomas Thrainer | checks. While the same can be accomplished in ExpandNames and/or
|
220 | 1a732a74 | Thomas Thrainer | CheckPrereq, doing these separate is better because:
|
221 | 1a732a74 | Thomas Thrainer |
|
222 | 1a732a74 | Thomas Thrainer | - ExpandNames is left as as purely a lock-related function
|
223 | 1a732a74 | Thomas Thrainer | - CheckPrereq is run after we have acquired locks (and possible
|
224 | 1a732a74 | Thomas Thrainer | waited for them)
|
225 | 1a732a74 | Thomas Thrainer |
|
226 | 1a732a74 | Thomas Thrainer | The function is allowed to change the self.op attribute so that
|
227 | 1a732a74 | Thomas Thrainer | later methods can no longer worry about missing parameters.
|
228 | 1a732a74 | Thomas Thrainer |
|
229 | 1a732a74 | Thomas Thrainer | """
|
230 | 1a732a74 | Thomas Thrainer | pass
|
231 | 1a732a74 | Thomas Thrainer | |
232 | 1a732a74 | Thomas Thrainer | def ExpandNames(self): |
233 | 1a732a74 | Thomas Thrainer | """Expand names for this LU.
|
234 | 1a732a74 | Thomas Thrainer |
|
235 | 1a732a74 | Thomas Thrainer | This method is called before starting to execute the opcode, and it should
|
236 | 1a732a74 | Thomas Thrainer | update all the parameters of the opcode to their canonical form (e.g. a
|
237 | 1a732a74 | Thomas Thrainer | short node name must be fully expanded after this method has successfully
|
238 | 1a732a74 | Thomas Thrainer | completed). This way locking, hooks, logging, etc. can work correctly.
|
239 | 1a732a74 | Thomas Thrainer |
|
240 | 1a732a74 | Thomas Thrainer | LUs which implement this method must also populate the self.needed_locks
|
241 | 1a732a74 | Thomas Thrainer | member, as a dict with lock levels as keys, and a list of needed lock names
|
242 | 1a732a74 | Thomas Thrainer | as values. Rules:
|
243 | 1a732a74 | Thomas Thrainer |
|
244 | 1a732a74 | Thomas Thrainer | - use an empty dict if you don't need any lock
|
245 | 1a732a74 | Thomas Thrainer | - if you don't need any lock at a particular level omit that
|
246 | 1a732a74 | Thomas Thrainer | level (note that in this case C{DeclareLocks} won't be called
|
247 | 1a732a74 | Thomas Thrainer | at all for that level)
|
248 | 1a732a74 | Thomas Thrainer | - if you need locks at a level, but you can't calculate it in
|
249 | 1a732a74 | Thomas Thrainer | this function, initialise that level with an empty list and do
|
250 | 1a732a74 | Thomas Thrainer | further processing in L{LogicalUnit.DeclareLocks} (see that
|
251 | 1a732a74 | Thomas Thrainer | function's docstring)
|
252 | 1a732a74 | Thomas Thrainer | - don't put anything for the BGL level
|
253 | 1a732a74 | Thomas Thrainer | - if you want all locks at a level use L{locking.ALL_SET} as a value
|
254 | 1a732a74 | Thomas Thrainer |
|
255 | 1a732a74 | Thomas Thrainer | If you need to share locks (rather than acquire them exclusively) at one
|
256 | 1a732a74 | Thomas Thrainer | level you can modify self.share_locks, setting a true value (usually 1) for
|
257 | 1a732a74 | Thomas Thrainer | that level. By default locks are not shared.
|
258 | 1a732a74 | Thomas Thrainer |
|
259 | 1a732a74 | Thomas Thrainer | This function can also define a list of tasklets, which then will be
|
260 | 1a732a74 | Thomas Thrainer | executed in order instead of the usual LU-level CheckPrereq and Exec
|
261 | 1a732a74 | Thomas Thrainer | functions, if those are not defined by the LU.
|
262 | 1a732a74 | Thomas Thrainer |
|
263 | 1a732a74 | Thomas Thrainer | Examples::
|
264 | 1a732a74 | Thomas Thrainer |
|
265 | 1a732a74 | Thomas Thrainer | # Acquire all nodes and one instance
|
266 | 1a732a74 | Thomas Thrainer | self.needed_locks = {
|
267 | 1a732a74 | Thomas Thrainer | locking.LEVEL_NODE: locking.ALL_SET,
|
268 | 1a732a74 | Thomas Thrainer | locking.LEVEL_INSTANCE: ['instance1.example.com'],
|
269 | 1a732a74 | Thomas Thrainer | }
|
270 | 1a732a74 | Thomas Thrainer | # Acquire just two nodes
|
271 | 1a732a74 | Thomas Thrainer | self.needed_locks = {
|
272 | 1c3231aa | Thomas Thrainer | locking.LEVEL_NODE: ['node1-uuid', 'node2-uuid'],
|
273 | 1a732a74 | Thomas Thrainer | }
|
274 | 1a732a74 | Thomas Thrainer | # Acquire no locks
|
275 | 1a732a74 | Thomas Thrainer | self.needed_locks = {} # No, you can't leave it to the default value None
|
276 | 1a732a74 | Thomas Thrainer |
|
277 | 1a732a74 | Thomas Thrainer | """
|
278 | 1a732a74 | Thomas Thrainer | # The implementation of this method is mandatory only if the new LU is
|
279 | 1a732a74 | Thomas Thrainer | # concurrent, so that old LUs don't need to be changed all at the same
|
280 | 1a732a74 | Thomas Thrainer | # time.
|
281 | 1a732a74 | Thomas Thrainer | if self.REQ_BGL: |
282 | 1a732a74 | Thomas Thrainer | self.needed_locks = {} # Exclusive LUs don't need locks. |
283 | 1a732a74 | Thomas Thrainer | else:
|
284 | 1a732a74 | Thomas Thrainer | raise NotImplementedError |
285 | 1a732a74 | Thomas Thrainer | |
286 | 1a732a74 | Thomas Thrainer | def DeclareLocks(self, level): |
287 | 1a732a74 | Thomas Thrainer | """Declare LU locking needs for a level
|
288 | 1a732a74 | Thomas Thrainer |
|
289 | 1a732a74 | Thomas Thrainer | While most LUs can just declare their locking needs at ExpandNames time,
|
290 | 1a732a74 | Thomas Thrainer | sometimes there's the need to calculate some locks after having acquired
|
291 | 1a732a74 | Thomas Thrainer | the ones before. This function is called just before acquiring locks at a
|
292 | 1a732a74 | Thomas Thrainer | particular level, but after acquiring the ones at lower levels, and permits
|
293 | 1a732a74 | Thomas Thrainer | such calculations. It can be used to modify self.needed_locks, and by
|
294 | 1a732a74 | Thomas Thrainer | default it does nothing.
|
295 | 1a732a74 | Thomas Thrainer |
|
296 | 1a732a74 | Thomas Thrainer | This function is only called if you have something already set in
|
297 | 1a732a74 | Thomas Thrainer | self.needed_locks for the level.
|
298 | 1a732a74 | Thomas Thrainer |
|
299 | 1a732a74 | Thomas Thrainer | @param level: Locking level which is going to be locked
|
300 | 1a732a74 | Thomas Thrainer | @type level: member of L{ganeti.locking.LEVELS}
|
301 | 1a732a74 | Thomas Thrainer |
|
302 | 1a732a74 | Thomas Thrainer | """
|
303 | 1a732a74 | Thomas Thrainer | |
304 | 1a732a74 | Thomas Thrainer | def CheckPrereq(self): |
305 | 1a732a74 | Thomas Thrainer | """Check prerequisites for this LU.
|
306 | 1a732a74 | Thomas Thrainer |
|
307 | 1a732a74 | Thomas Thrainer | This method should check that the prerequisites for the execution
|
308 | 1a732a74 | Thomas Thrainer | of this LU are fulfilled. It can do internode communication, but
|
309 | 1a732a74 | Thomas Thrainer | it should be idempotent - no cluster or system changes are
|
310 | 1a732a74 | Thomas Thrainer | allowed.
|
311 | 1a732a74 | Thomas Thrainer |
|
312 | 1a732a74 | Thomas Thrainer | The method should raise errors.OpPrereqError in case something is
|
313 | 1a732a74 | Thomas Thrainer | not fulfilled. Its return value is ignored.
|
314 | 1a732a74 | Thomas Thrainer |
|
315 | 1a732a74 | Thomas Thrainer | This method should also update all the parameters of the opcode to
|
316 | 1a732a74 | Thomas Thrainer | their canonical form if it hasn't been done by ExpandNames before.
|
317 | 1a732a74 | Thomas Thrainer |
|
318 | 1a732a74 | Thomas Thrainer | """
|
319 | 1a732a74 | Thomas Thrainer | if self.tasklets is not None: |
320 | 1a732a74 | Thomas Thrainer | for (idx, tl) in enumerate(self.tasklets): |
321 | 1a732a74 | Thomas Thrainer | logging.debug("Checking prerequisites for tasklet %s/%s",
|
322 | 1a732a74 | Thomas Thrainer | idx + 1, len(self.tasklets)) |
323 | 1a732a74 | Thomas Thrainer | tl.CheckPrereq() |
324 | 1a732a74 | Thomas Thrainer | else:
|
325 | 1a732a74 | Thomas Thrainer | pass
|
326 | 1a732a74 | Thomas Thrainer | |
327 | 1a732a74 | Thomas Thrainer | def Exec(self, feedback_fn): |
328 | 1a732a74 | Thomas Thrainer | """Execute the LU.
|
329 | 1a732a74 | Thomas Thrainer |
|
330 | 1a732a74 | Thomas Thrainer | This method should implement the actual work. It should raise
|
331 | 1a732a74 | Thomas Thrainer | errors.OpExecError for failures that are somewhat dealt with in
|
332 | 1a732a74 | Thomas Thrainer | code, or expected.
|
333 | 1a732a74 | Thomas Thrainer |
|
334 | 1a732a74 | Thomas Thrainer | """
|
335 | 1a732a74 | Thomas Thrainer | if self.tasklets is not None: |
336 | 1a732a74 | Thomas Thrainer | for (idx, tl) in enumerate(self.tasklets): |
337 | 1a732a74 | Thomas Thrainer | logging.debug("Executing tasklet %s/%s", idx + 1, len(self.tasklets)) |
338 | 1a732a74 | Thomas Thrainer | tl.Exec(feedback_fn) |
339 | 1a732a74 | Thomas Thrainer | else:
|
340 | 1a732a74 | Thomas Thrainer | raise NotImplementedError |
341 | 1a732a74 | Thomas Thrainer | |
342 | 1a732a74 | Thomas Thrainer | def BuildHooksEnv(self): |
343 | 1a732a74 | Thomas Thrainer | """Build hooks environment for this LU.
|
344 | 1a732a74 | Thomas Thrainer |
|
345 | 1a732a74 | Thomas Thrainer | @rtype: dict
|
346 | 1a732a74 | Thomas Thrainer | @return: Dictionary containing the environment that will be used for
|
347 | 1a732a74 | Thomas Thrainer | running the hooks for this LU. The keys of the dict must not be prefixed
|
348 | 1a732a74 | Thomas Thrainer | with "GANETI_"--that'll be added by the hooks runner. The hooks runner
|
349 | 1a732a74 | Thomas Thrainer | will extend the environment with additional variables. If no environment
|
350 | 1a732a74 | Thomas Thrainer | should be defined, an empty dictionary should be returned (not C{None}).
|
351 | 1a732a74 | Thomas Thrainer | @note: If the C{HPATH} attribute of the LU class is C{None}, this function
|
352 | 1a732a74 | Thomas Thrainer | will not be called.
|
353 | 1a732a74 | Thomas Thrainer |
|
354 | 1a732a74 | Thomas Thrainer | """
|
355 | 1a732a74 | Thomas Thrainer | raise NotImplementedError |
356 | 1a732a74 | Thomas Thrainer | |
357 | 1a732a74 | Thomas Thrainer | def BuildHooksNodes(self): |
358 | 1a732a74 | Thomas Thrainer | """Build list of nodes to run LU's hooks.
|
359 | 1a732a74 | Thomas Thrainer |
|
360 | ff1c051b | Thomas Thrainer | @rtype: tuple; (list, list)
|
361 | 1c3231aa | Thomas Thrainer | @return: Tuple containing a list of node UUIDs on which the hook
|
362 | 1c3231aa | Thomas Thrainer | should run before the execution and a list of node UUIDs on which the
|
363 | ff1c051b | Thomas Thrainer | hook should run after the execution.
|
364 | 1c3231aa | Thomas Thrainer | No nodes should be returned as an empty list (and not None).
|
365 | 1a732a74 | Thomas Thrainer | @note: If the C{HPATH} attribute of the LU class is C{None}, this function
|
366 | 1a732a74 | Thomas Thrainer | will not be called.
|
367 | 1a732a74 | Thomas Thrainer |
|
368 | 1a732a74 | Thomas Thrainer | """
|
369 | 1a732a74 | Thomas Thrainer | raise NotImplementedError |
370 | 1a732a74 | Thomas Thrainer | |
371 | 237a833c | Thomas Thrainer | def PreparePostHookNodes(self, post_hook_node_uuids): |
372 | 237a833c | Thomas Thrainer | """Extend list of nodes to run the post LU hook.
|
373 | 237a833c | Thomas Thrainer |
|
374 | 237a833c | Thomas Thrainer | This method allows LUs to change the list of node UUIDs on which the
|
375 | 237a833c | Thomas Thrainer | post hook should run after the LU has been executed but before the post
|
376 | 237a833c | Thomas Thrainer | hook is run.
|
377 | 237a833c | Thomas Thrainer |
|
378 | 237a833c | Thomas Thrainer | @type post_hook_node_uuids: list
|
379 | 237a833c | Thomas Thrainer | @param post_hook_node_uuids: The initial list of node UUIDs to run the
|
380 | 237a833c | Thomas Thrainer | post hook on, as returned by L{BuildHooksNodes}.
|
381 | 237a833c | Thomas Thrainer | @rtype: list
|
382 | 237a833c | Thomas Thrainer | @return: list of node UUIDs on which the post hook should run. The default
|
383 | 237a833c | Thomas Thrainer | implementation returns the passed in C{post_hook_node_uuids}, but
|
384 | 237a833c | Thomas Thrainer | custom implementations can choose to alter the list.
|
385 | 237a833c | Thomas Thrainer |
|
386 | 237a833c | Thomas Thrainer | """
|
387 | 237a833c | Thomas Thrainer | # For consistency with HooksCallBack we ignore the "could be a function"
|
388 | 237a833c | Thomas Thrainer | # warning
|
389 | 237a833c | Thomas Thrainer | # pylint: disable=R0201
|
390 | 237a833c | Thomas Thrainer | return post_hook_node_uuids
|
391 | 237a833c | Thomas Thrainer | |
392 | 1a732a74 | Thomas Thrainer | def HooksCallBack(self, phase, hook_results, feedback_fn, lu_result): |
393 | 1a732a74 | Thomas Thrainer | """Notify the LU about the results of its hooks.
|
394 | 1a732a74 | Thomas Thrainer |
|
395 | 1a732a74 | Thomas Thrainer | This method is called every time a hooks phase is executed, and notifies
|
396 | 1a732a74 | Thomas Thrainer | the Logical Unit about the hooks' result. The LU can then use it to alter
|
397 | 1a732a74 | Thomas Thrainer | its result based on the hooks. By default the method does nothing and the
|
398 | 1a732a74 | Thomas Thrainer | previous result is passed back unchanged but any LU can define it if it
|
399 | 1a732a74 | Thomas Thrainer | wants to use the local cluster hook-scripts somehow.
|
400 | 1a732a74 | Thomas Thrainer |
|
401 | 1a732a74 | Thomas Thrainer | @param phase: one of L{constants.HOOKS_PHASE_POST} or
|
402 | 1a732a74 | Thomas Thrainer | L{constants.HOOKS_PHASE_PRE}; it denotes the hooks phase
|
403 | 1a732a74 | Thomas Thrainer | @param hook_results: the results of the multi-node hooks rpc call
|
404 | 1a732a74 | Thomas Thrainer | @param feedback_fn: function used send feedback back to the caller
|
405 | 1a732a74 | Thomas Thrainer | @param lu_result: the previous Exec result this LU had, or None
|
406 | 1a732a74 | Thomas Thrainer | in the PRE phase
|
407 | 1a732a74 | Thomas Thrainer | @return: the new Exec result, based on the previous result
|
408 | 1a732a74 | Thomas Thrainer | and hook results
|
409 | 1a732a74 | Thomas Thrainer |
|
410 | 1a732a74 | Thomas Thrainer | """
|
411 | 1a732a74 | Thomas Thrainer | # API must be kept, thus we ignore the unused argument and could
|
412 | 1a732a74 | Thomas Thrainer | # be a function warnings
|
413 | 1a732a74 | Thomas Thrainer | # pylint: disable=W0613,R0201
|
414 | 1a732a74 | Thomas Thrainer | return lu_result
|
415 | 1a732a74 | Thomas Thrainer | |
416 | 1a732a74 | Thomas Thrainer | def _ExpandAndLockInstance(self): |
417 | 1a732a74 | Thomas Thrainer | """Helper function to expand and lock an instance.
|
418 | 1a732a74 | Thomas Thrainer |
|
419 | 1a732a74 | Thomas Thrainer | Many LUs that work on an instance take its name in self.op.instance_name
|
420 | 1a732a74 | Thomas Thrainer | and need to expand it and then declare the expanded name for locking. This
|
421 | 1a732a74 | Thomas Thrainer | function does it, and then updates self.op.instance_name to the expanded
|
422 | 1a732a74 | Thomas Thrainer | name. It also initializes needed_locks as a dict, if this hasn't been done
|
423 | 1a732a74 | Thomas Thrainer | before.
|
424 | 1a732a74 | Thomas Thrainer |
|
425 | 1a732a74 | Thomas Thrainer | """
|
426 | 1a732a74 | Thomas Thrainer | if self.needed_locks is None: |
427 | 1a732a74 | Thomas Thrainer | self.needed_locks = {}
|
428 | 1a732a74 | Thomas Thrainer | else:
|
429 | 1a732a74 | Thomas Thrainer | assert locking.LEVEL_INSTANCE not in self.needed_locks, \ |
430 | 1a732a74 | Thomas Thrainer | "_ExpandAndLockInstance called with instance-level locks set"
|
431 | da4a52a3 | Thomas Thrainer | (self.op.instance_uuid, self.op.instance_name) = \ |
432 | da4a52a3 | Thomas Thrainer | ExpandInstanceUuidAndName(self.cfg, self.op.instance_uuid, |
433 | da4a52a3 | Thomas Thrainer | self.op.instance_name)
|
434 | 1a732a74 | Thomas Thrainer | self.needed_locks[locking.LEVEL_INSTANCE] = self.op.instance_name |
435 | 1a732a74 | Thomas Thrainer | |
436 | 1a732a74 | Thomas Thrainer | def _LockInstancesNodes(self, primary_only=False, |
437 | 1a732a74 | Thomas Thrainer | level=locking.LEVEL_NODE): |
438 | 1a732a74 | Thomas Thrainer | """Helper function to declare instances' nodes for locking.
|
439 | 1a732a74 | Thomas Thrainer |
|
440 | 1a732a74 | Thomas Thrainer | This function should be called after locking one or more instances to lock
|
441 | 1a732a74 | Thomas Thrainer | their nodes. Its effect is populating self.needed_locks[locking.LEVEL_NODE]
|
442 | 1a732a74 | Thomas Thrainer | with all primary or secondary nodes for instances already locked and
|
443 | 1a732a74 | Thomas Thrainer | present in self.needed_locks[locking.LEVEL_INSTANCE].
|
444 | 1a732a74 | Thomas Thrainer |
|
445 | 1a732a74 | Thomas Thrainer | It should be called from DeclareLocks, and for safety only works if
|
446 | 1a732a74 | Thomas Thrainer | self.recalculate_locks[locking.LEVEL_NODE] is set.
|
447 | 1a732a74 | Thomas Thrainer |
|
448 | 1a732a74 | Thomas Thrainer | In the future it may grow parameters to just lock some instance's nodes, or
|
449 | 1a732a74 | Thomas Thrainer | to just lock primaries or secondary nodes, if needed.
|
450 | 1a732a74 | Thomas Thrainer |
|
451 | 1a732a74 | Thomas Thrainer | If should be called in DeclareLocks in a way similar to::
|
452 | 1a732a74 | Thomas Thrainer |
|
453 | 1a732a74 | Thomas Thrainer | if level == locking.LEVEL_NODE:
|
454 | 1a732a74 | Thomas Thrainer | self._LockInstancesNodes()
|
455 | 1a732a74 | Thomas Thrainer |
|
456 | 1a732a74 | Thomas Thrainer | @type primary_only: boolean
|
457 | 1a732a74 | Thomas Thrainer | @param primary_only: only lock primary nodes of locked instances
|
458 | 1a732a74 | Thomas Thrainer | @param level: Which lock level to use for locking nodes
|
459 | 1a732a74 | Thomas Thrainer |
|
460 | 1a732a74 | Thomas Thrainer | """
|
461 | 1a732a74 | Thomas Thrainer | assert level in self.recalculate_locks, \ |
462 | 1a732a74 | Thomas Thrainer | "_LockInstancesNodes helper function called with no nodes to recalculate"
|
463 | 1a732a74 | Thomas Thrainer | |
464 | 1a732a74 | Thomas Thrainer | # TODO: check if we're really been called with the instance locks held
|
465 | 1a732a74 | Thomas Thrainer | |
466 | 1a732a74 | Thomas Thrainer | # For now we'll replace self.needed_locks[locking.LEVEL_NODE], but in the
|
467 | 1a732a74 | Thomas Thrainer | # future we might want to have different behaviors depending on the value
|
468 | 1a732a74 | Thomas Thrainer | # of self.recalculate_locks[locking.LEVEL_NODE]
|
469 | 1c3231aa | Thomas Thrainer | wanted_node_uuids = [] |
470 | 1a732a74 | Thomas Thrainer | locked_i = self.owned_locks(locking.LEVEL_INSTANCE)
|
471 | da4a52a3 | Thomas Thrainer | for _, instance in self.cfg.GetMultiInstanceInfoByName(locked_i): |
472 | 1c3231aa | Thomas Thrainer | wanted_node_uuids.append(instance.primary_node) |
473 | 1a732a74 | Thomas Thrainer | if not primary_only: |
474 | 1c3231aa | Thomas Thrainer | wanted_node_uuids.extend(instance.secondary_nodes) |
475 | 1a732a74 | Thomas Thrainer | |
476 | 1a732a74 | Thomas Thrainer | if self.recalculate_locks[level] == constants.LOCKS_REPLACE: |
477 | 1c3231aa | Thomas Thrainer | self.needed_locks[level] = wanted_node_uuids
|
478 | 1a732a74 | Thomas Thrainer | elif self.recalculate_locks[level] == constants.LOCKS_APPEND: |
479 | 1c3231aa | Thomas Thrainer | self.needed_locks[level].extend(wanted_node_uuids)
|
480 | 1a732a74 | Thomas Thrainer | else:
|
481 | 1a732a74 | Thomas Thrainer | raise errors.ProgrammerError("Unknown recalculation mode") |
482 | 1a732a74 | Thomas Thrainer | |
483 | 1a732a74 | Thomas Thrainer | del self.recalculate_locks[level] |
484 | 1a732a74 | Thomas Thrainer | |
485 | 1a732a74 | Thomas Thrainer | |
486 | 1a732a74 | Thomas Thrainer | class NoHooksLU(LogicalUnit): # pylint: disable=W0223 |
487 | 1a732a74 | Thomas Thrainer | """Simple LU which runs no hooks.
|
488 | 1a732a74 | Thomas Thrainer |
|
489 | 1a732a74 | Thomas Thrainer | This LU is intended as a parent for other LogicalUnits which will
|
490 | 1a732a74 | Thomas Thrainer | run no hooks, in order to reduce duplicate code.
|
491 | 1a732a74 | Thomas Thrainer |
|
492 | 1a732a74 | Thomas Thrainer | """
|
493 | 1a732a74 | Thomas Thrainer | HPATH = None
|
494 | 1a732a74 | Thomas Thrainer | HTYPE = None
|
495 | 1a732a74 | Thomas Thrainer | |
496 | 1a732a74 | Thomas Thrainer | def BuildHooksEnv(self): |
497 | 1a732a74 | Thomas Thrainer | """Empty BuildHooksEnv for NoHooksLu.
|
498 | 1a732a74 | Thomas Thrainer |
|
499 | 1a732a74 | Thomas Thrainer | This just raises an error.
|
500 | 1a732a74 | Thomas Thrainer |
|
501 | 1a732a74 | Thomas Thrainer | """
|
502 | 1a732a74 | Thomas Thrainer | raise AssertionError("BuildHooksEnv called for NoHooksLUs") |
503 | 1a732a74 | Thomas Thrainer | |
504 | 1a732a74 | Thomas Thrainer | def BuildHooksNodes(self): |
505 | 1a732a74 | Thomas Thrainer | """Empty BuildHooksNodes for NoHooksLU.
|
506 | 1a732a74 | Thomas Thrainer |
|
507 | 1a732a74 | Thomas Thrainer | """
|
508 | 1a732a74 | Thomas Thrainer | raise AssertionError("BuildHooksNodes called for NoHooksLU") |
509 | 1a732a74 | Thomas Thrainer | |
510 | 237a833c | Thomas Thrainer | def PreparePostHookNodes(self, post_hook_node_uuids): |
511 | 237a833c | Thomas Thrainer | """Empty PreparePostHookNodes for NoHooksLU.
|
512 | 237a833c | Thomas Thrainer |
|
513 | 237a833c | Thomas Thrainer | """
|
514 | 237a833c | Thomas Thrainer | raise AssertionError("PreparePostHookNodes called for NoHooksLU") |
515 | 237a833c | Thomas Thrainer | |
516 | 1a732a74 | Thomas Thrainer | |
517 | 1a732a74 | Thomas Thrainer | class Tasklet: |
518 | 1a732a74 | Thomas Thrainer | """Tasklet base class.
|
519 | 1a732a74 | Thomas Thrainer |
|
520 | 1a732a74 | Thomas Thrainer | Tasklets are subcomponents for LUs. LUs can consist entirely of tasklets or
|
521 | 1a732a74 | Thomas Thrainer | they can mix legacy code with tasklets. Locking needs to be done in the LU,
|
522 | 1a732a74 | Thomas Thrainer | tasklets know nothing about locks.
|
523 | 1a732a74 | Thomas Thrainer |
|
524 | 1a732a74 | Thomas Thrainer | Subclasses must follow these rules:
|
525 | 1a732a74 | Thomas Thrainer | - Implement CheckPrereq
|
526 | 1a732a74 | Thomas Thrainer | - Implement Exec
|
527 | 1a732a74 | Thomas Thrainer |
|
528 | 1a732a74 | Thomas Thrainer | """
|
529 | 1a732a74 | Thomas Thrainer | def __init__(self, lu): |
530 | 1a732a74 | Thomas Thrainer | self.lu = lu
|
531 | 1a732a74 | Thomas Thrainer | |
532 | 1a732a74 | Thomas Thrainer | # Shortcuts
|
533 | 1a732a74 | Thomas Thrainer | self.cfg = lu.cfg
|
534 | 1a732a74 | Thomas Thrainer | self.rpc = lu.rpc
|
535 | 1a732a74 | Thomas Thrainer | |
536 | 1a732a74 | Thomas Thrainer | def CheckPrereq(self): |
537 | 1a732a74 | Thomas Thrainer | """Check prerequisites for this tasklets.
|
538 | 1a732a74 | Thomas Thrainer |
|
539 | 1a732a74 | Thomas Thrainer | This method should check whether the prerequisites for the execution of
|
540 | 1a732a74 | Thomas Thrainer | this tasklet are fulfilled. It can do internode communication, but it
|
541 | 1a732a74 | Thomas Thrainer | should be idempotent - no cluster or system changes are allowed.
|
542 | 1a732a74 | Thomas Thrainer |
|
543 | 1a732a74 | Thomas Thrainer | The method should raise errors.OpPrereqError in case something is not
|
544 | 1a732a74 | Thomas Thrainer | fulfilled. Its return value is ignored.
|
545 | 1a732a74 | Thomas Thrainer |
|
546 | 1a732a74 | Thomas Thrainer | This method should also update all parameters to their canonical form if it
|
547 | 1a732a74 | Thomas Thrainer | hasn't been done before.
|
548 | 1a732a74 | Thomas Thrainer |
|
549 | 1a732a74 | Thomas Thrainer | """
|
550 | 1a732a74 | Thomas Thrainer | pass
|
551 | 1a732a74 | Thomas Thrainer | |
552 | 1a732a74 | Thomas Thrainer | def Exec(self, feedback_fn): |
553 | 1a732a74 | Thomas Thrainer | """Execute the tasklet.
|
554 | 1a732a74 | Thomas Thrainer |
|
555 | 1a732a74 | Thomas Thrainer | This method should implement the actual work. It should raise
|
556 | 1a732a74 | Thomas Thrainer | errors.OpExecError for failures that are somewhat dealt with in code, or
|
557 | 1a732a74 | Thomas Thrainer | expected.
|
558 | 1a732a74 | Thomas Thrainer |
|
559 | 1a732a74 | Thomas Thrainer | """
|
560 | 1a732a74 | Thomas Thrainer | raise NotImplementedError |
561 | 1a732a74 | Thomas Thrainer | |
562 | 1a732a74 | Thomas Thrainer | |
563 | 5eacbcae | Thomas Thrainer | class QueryBase: |
564 | 1a732a74 | Thomas Thrainer | """Base for query utility classes.
|
565 | 1a732a74 | Thomas Thrainer |
|
566 | 1a732a74 | Thomas Thrainer | """
|
567 | 1a732a74 | Thomas Thrainer | #: Attribute holding field definitions
|
568 | 1a732a74 | Thomas Thrainer | FIELDS = None
|
569 | 1a732a74 | Thomas Thrainer | |
570 | 1a732a74 | Thomas Thrainer | #: Field to sort by
|
571 | 1a732a74 | Thomas Thrainer | SORT_FIELD = "name"
|
572 | 1a732a74 | Thomas Thrainer | |
573 | 1a732a74 | Thomas Thrainer | def __init__(self, qfilter, fields, use_locking): |
574 | 1a732a74 | Thomas Thrainer | """Initializes this class.
|
575 | 1a732a74 | Thomas Thrainer |
|
576 | 1a732a74 | Thomas Thrainer | """
|
577 | 1a732a74 | Thomas Thrainer | self.use_locking = use_locking
|
578 | 1a732a74 | Thomas Thrainer | |
579 | 1a732a74 | Thomas Thrainer | self.query = query.Query(self.FIELDS, fields, qfilter=qfilter, |
580 | 1a732a74 | Thomas Thrainer | namefield=self.SORT_FIELD)
|
581 | 1a732a74 | Thomas Thrainer | self.requested_data = self.query.RequestedData() |
582 | 1a732a74 | Thomas Thrainer | self.names = self.query.RequestedNames() |
583 | 1a732a74 | Thomas Thrainer | |
584 | 1a732a74 | Thomas Thrainer | # Sort only if no names were requested
|
585 | 1a732a74 | Thomas Thrainer | self.sort_by_name = not self.names |
586 | 1a732a74 | Thomas Thrainer | |
587 | 1a732a74 | Thomas Thrainer | self.do_locking = None |
588 | 1a732a74 | Thomas Thrainer | self.wanted = None |
589 | 1a732a74 | Thomas Thrainer | |
590 | 1a732a74 | Thomas Thrainer | def _GetNames(self, lu, all_names, lock_level): |
591 | 1a732a74 | Thomas Thrainer | """Helper function to determine names asked for in the query.
|
592 | 1a732a74 | Thomas Thrainer |
|
593 | 1a732a74 | Thomas Thrainer | """
|
594 | 1a732a74 | Thomas Thrainer | if self.do_locking: |
595 | 1a732a74 | Thomas Thrainer | names = lu.owned_locks(lock_level) |
596 | 1a732a74 | Thomas Thrainer | else:
|
597 | 1a732a74 | Thomas Thrainer | names = all_names |
598 | 1a732a74 | Thomas Thrainer | |
599 | 1a732a74 | Thomas Thrainer | if self.wanted == locking.ALL_SET: |
600 | 1a732a74 | Thomas Thrainer | assert not self.names |
601 | 1a732a74 | Thomas Thrainer | # caller didn't specify names, so ordering is not important
|
602 | 1a732a74 | Thomas Thrainer | return utils.NiceSort(names)
|
603 | 1a732a74 | Thomas Thrainer | |
604 | 1a732a74 | Thomas Thrainer | # caller specified names and we must keep the same order
|
605 | 1a732a74 | Thomas Thrainer | assert self.names |
606 | 1a732a74 | Thomas Thrainer | |
607 | 1a732a74 | Thomas Thrainer | missing = set(self.wanted).difference(names) |
608 | 1a732a74 | Thomas Thrainer | if missing:
|
609 | 1a732a74 | Thomas Thrainer | raise errors.OpExecError("Some items were removed before retrieving" |
610 | 1a732a74 | Thomas Thrainer | " their data: %s" % missing)
|
611 | 1a732a74 | Thomas Thrainer | |
612 | 1a732a74 | Thomas Thrainer | # Return expanded names
|
613 | 1a732a74 | Thomas Thrainer | return self.wanted |
614 | 1a732a74 | Thomas Thrainer | |
615 | 1a732a74 | Thomas Thrainer | def ExpandNames(self, lu): |
616 | 1a732a74 | Thomas Thrainer | """Expand names for this query.
|
617 | 1a732a74 | Thomas Thrainer |
|
618 | 1a732a74 | Thomas Thrainer | See L{LogicalUnit.ExpandNames}.
|
619 | 1a732a74 | Thomas Thrainer |
|
620 | 1a732a74 | Thomas Thrainer | """
|
621 | 1a732a74 | Thomas Thrainer | raise NotImplementedError() |
622 | 1a732a74 | Thomas Thrainer | |
623 | 1a732a74 | Thomas Thrainer | def DeclareLocks(self, lu, level): |
624 | 1a732a74 | Thomas Thrainer | """Declare locks for this query.
|
625 | 1a732a74 | Thomas Thrainer |
|
626 | 1a732a74 | Thomas Thrainer | See L{LogicalUnit.DeclareLocks}.
|
627 | 1a732a74 | Thomas Thrainer |
|
628 | 1a732a74 | Thomas Thrainer | """
|
629 | 1a732a74 | Thomas Thrainer | raise NotImplementedError() |
630 | 1a732a74 | Thomas Thrainer | |
631 | 1a732a74 | Thomas Thrainer | def _GetQueryData(self, lu): |
632 | 1a732a74 | Thomas Thrainer | """Collects all data for this query.
|
633 | 1a732a74 | Thomas Thrainer |
|
634 | 1a732a74 | Thomas Thrainer | @return: Query data object
|
635 | 1a732a74 | Thomas Thrainer |
|
636 | 1a732a74 | Thomas Thrainer | """
|
637 | 1a732a74 | Thomas Thrainer | raise NotImplementedError() |
638 | 1a732a74 | Thomas Thrainer | |
639 | 1a732a74 | Thomas Thrainer | def NewStyleQuery(self, lu): |
640 | 1a732a74 | Thomas Thrainer | """Collect data and execute query.
|
641 | 1a732a74 | Thomas Thrainer |
|
642 | 1a732a74 | Thomas Thrainer | """
|
643 | 1a732a74 | Thomas Thrainer | return query.GetQueryResponse(self.query, self._GetQueryData(lu), |
644 | 1a732a74 | Thomas Thrainer | sort_by_name=self.sort_by_name)
|
645 | 1a732a74 | Thomas Thrainer | |
646 | 1a732a74 | Thomas Thrainer | def OldStyleQuery(self, lu): |
647 | 1a732a74 | Thomas Thrainer | """Collect data and execute query.
|
648 | 1a732a74 | Thomas Thrainer |
|
649 | 1a732a74 | Thomas Thrainer | """
|
650 | 1a732a74 | Thomas Thrainer | return self.query.OldStyleQuery(self._GetQueryData(lu), |
651 | 1a732a74 | Thomas Thrainer | sort_by_name=self.sort_by_name) |