Package ganeti :: Package cmdlib :: Module test
[hide private]
[frames] | no frames]

Source Code for Module ganeti.cmdlib.test

  1  # 
  2  # 
  3   
  4  # Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Google Inc. 
  5  # All rights reserved. 
  6  # 
  7  # Redistribution and use in source and binary forms, with or without 
  8  # modification, are permitted provided that the following conditions are 
  9  # met: 
 10  # 
 11  # 1. Redistributions of source code must retain the above copyright notice, 
 12  # this list of conditions and the following disclaimer. 
 13  # 
 14  # 2. Redistributions in binary form must reproduce the above copyright 
 15  # notice, this list of conditions and the following disclaimer in the 
 16  # documentation and/or other materials provided with the distribution. 
 17  # 
 18  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
 19  # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
 20  # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
 21  # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
 22  # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 23  # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 24  # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
 25  # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
 26  # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
 27  # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 28  # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 29   
 30   
 31  """Test logical units.""" 
 32   
 33  import logging 
 34  import shutil 
 35  import socket 
 36  import tempfile 
 37   
 38  from ganeti import compat 
 39  from ganeti import constants 
 40  from ganeti import errors 
 41  from ganeti import locking 
 42  from ganeti import utils 
 43  from ganeti.masterd import iallocator 
 44  from ganeti.cmdlib.base import NoHooksLU 
 45  from ganeti.cmdlib.common import ExpandInstanceUuidAndName, GetWantedNodes, \ 
 46    GetWantedInstances 
47 48 49 -class LUTestDelay(NoHooksLU):
50 """Sleep for a specified amount of time. 51 52 This LU sleeps on the master and/or nodes for a specified amount of 53 time. 54 55 """ 56 REQ_BGL = False 57
58 - def ExpandNames(self):
59 """Expand names and set required locks. 60 61 This expands the node list, if any. 62 63 """ 64 self.needed_locks = {} 65 66 if not self.op.no_locks and (self.op.on_nodes or self.op.on_master): 67 self.needed_locks[locking.LEVEL_NODE] = [] 68 69 if self.op.on_nodes: 70 # _GetWantedNodes can be used here, but is not always appropriate to use 71 # this way in ExpandNames. Check LogicalUnit.ExpandNames docstring for 72 # more information. 73 (self.op.on_node_uuids, self.op.on_nodes) = \ 74 GetWantedNodes(self, self.op.on_nodes) 75 if not self.op.no_locks: 76 self.needed_locks[locking.LEVEL_NODE].extend(self.op.on_node_uuids) 77 78 if not self.op.no_locks and self.op.on_master: 79 # The node lock should be acquired for the master as well. 80 self.needed_locks[locking.LEVEL_NODE].append(self.cfg.GetMasterNode())
81
82 - def _TestDelay(self):
83 """Do the actual sleep. 84 85 """ 86 if self.op.on_master: 87 if not utils.TestDelay(self.op.duration)[0]: 88 raise errors.OpExecError("Error during master delay test") 89 if self.op.on_node_uuids: 90 result = self.rpc.call_test_delay(self.op.on_node_uuids, self.op.duration) 91 for node_uuid, node_result in result.items(): 92 node_result.Raise("Failure during rpc call to node %s" % 93 self.cfg.GetNodeName(node_uuid))
94
95 - def Exec(self, feedback_fn):
96 """Execute the test delay opcode, with the wanted repetitions. 97 98 """ 99 if self.op.repeat == 0: 100 self._TestDelay() 101 else: 102 top_value = self.op.repeat - 1 103 for i in range(self.op.repeat): 104 self.LogInfo("Test delay iteration %d/%d", i, top_value) 105 self._TestDelay()
106
107 108 -class LUTestJqueue(NoHooksLU):
109 """Utility LU to test some aspects of the job queue. 110 111 """ 112 REQ_BGL = False 113 114 # Must be lower than default timeout for WaitForJobChange to see whether it 115 # notices changed jobs 116 _CLIENT_CONNECT_TIMEOUT = 20.0 117 _CLIENT_CONFIRM_TIMEOUT = 60.0 118 119 @classmethod
120 - def _NotifyUsingSocket(cls, cb, errcls):
121 """Opens a Unix socket and waits for another program to connect. 122 123 @type cb: callable 124 @param cb: Callback to send socket name to client 125 @type errcls: class 126 @param errcls: Exception class to use for errors 127 128 """ 129 # Using a temporary directory as there's no easy way to create temporary 130 # sockets without writing a custom loop around tempfile.mktemp and 131 # socket.bind 132 tmpdir = tempfile.mkdtemp() 133 try: 134 tmpsock = utils.PathJoin(tmpdir, "sock") 135 136 logging.debug("Creating temporary socket at %s", tmpsock) 137 sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 138 try: 139 sock.bind(tmpsock) 140 sock.listen(1) 141 142 # Send details to client 143 cb(tmpsock) 144 145 # Wait for client to connect before continuing 146 sock.settimeout(cls._CLIENT_CONNECT_TIMEOUT) 147 try: 148 (conn, _) = sock.accept() 149 except socket.error, err: 150 raise errcls("Client didn't connect in time (%s)" % err) 151 finally: 152 sock.close() 153 finally: 154 # Remove as soon as client is connected 155 shutil.rmtree(tmpdir) 156 157 # Wait for client to close 158 try: 159 try: 160 # pylint: disable=E1101 161 # Instance of '_socketobject' has no ... member 162 conn.settimeout(cls._CLIENT_CONFIRM_TIMEOUT) 163 conn.recv(1) 164 except socket.error, err: 165 raise errcls("Client failed to confirm notification (%s)" % err) 166 finally: 167 conn.close()
168
169 - def _SendNotification(self, test, arg, sockname):
170 """Sends a notification to the client. 171 172 @type test: string 173 @param test: Test name 174 @param arg: Test argument (depends on test) 175 @type sockname: string 176 @param sockname: Socket path 177 178 """ 179 self.Log(constants.ELOG_JQUEUE_TEST, (sockname, test, arg))
180
181 - def _Notify(self, prereq, test, arg):
182 """Notifies the client of a test. 183 184 @type prereq: bool 185 @param prereq: Whether this is a prereq-phase test 186 @type test: string 187 @param test: Test name 188 @param arg: Test argument (depends on test) 189 190 """ 191 if prereq: 192 errcls = errors.OpPrereqError 193 else: 194 errcls = errors.OpExecError 195 196 return self._NotifyUsingSocket(compat.partial(self._SendNotification, 197 test, arg), 198 errcls)
199
200 - def CheckArguments(self):
201 self.checkargs_calls = getattr(self, "checkargs_calls", 0) + 1 202 self.expandnames_calls = 0
203
204 - def ExpandNames(self):
205 checkargs_calls = getattr(self, "checkargs_calls", 0) 206 if checkargs_calls < 1: 207 raise errors.ProgrammerError("CheckArguments was not called") 208 209 self.expandnames_calls += 1 210 211 if self.op.notify_waitlock: 212 self._Notify(True, constants.JQT_EXPANDNAMES, None) 213 214 self.LogInfo("Expanding names") 215 216 # Get lock on master node (just to get a lock, not for a particular reason) 217 self.needed_locks = { 218 locking.LEVEL_NODE: self.cfg.GetMasterNode(), 219 }
220
221 - def Exec(self, feedback_fn):
222 if self.expandnames_calls < 1: 223 raise errors.ProgrammerError("ExpandNames was not called") 224 225 if self.op.notify_exec: 226 self._Notify(False, constants.JQT_EXEC, None) 227 228 self.LogInfo("Executing") 229 230 if self.op.log_messages: 231 self._Notify(False, constants.JQT_STARTMSG, len(self.op.log_messages)) 232 for idx, msg in enumerate(self.op.log_messages): 233 self.LogInfo("Sending log message %s", idx + 1) 234 feedback_fn(constants.JQT_MSGPREFIX + msg) 235 # Report how many test messages have been sent 236 self._Notify(False, constants.JQT_LOGMSG, idx + 1) 237 238 if self.op.fail: 239 raise errors.OpExecError("Opcode failure was requested") 240 241 return True
242
243 244 -class LUTestAllocator(NoHooksLU):
245 """Run allocator tests. 246 247 This LU runs the allocator tests 248 249 """
250 - def CheckPrereq(self):
251 """Check prerequisites. 252 253 This checks the opcode parameters depending on the director and mode test. 254 255 """ 256 if self.op.mode in (constants.IALLOCATOR_MODE_ALLOC, 257 constants.IALLOCATOR_MODE_MULTI_ALLOC): 258 (self.inst_uuid, iname) = self.cfg.ExpandInstanceName(self.op.name) 259 if iname is not None: 260 raise errors.OpPrereqError("Instance '%s' already in the cluster" % 261 iname, errors.ECODE_EXISTS) 262 for row in self.op.disks: 263 if (not isinstance(row, dict) or 264 constants.IDISK_SIZE not in row or 265 not isinstance(row[constants.IDISK_SIZE], int) or 266 constants.IDISK_MODE not in row or 267 row[constants.IDISK_MODE] not in constants.DISK_ACCESS_SET): 268 raise errors.OpPrereqError("Invalid contents of the 'disks'" 269 " parameter", errors.ECODE_INVAL) 270 if self.op.hypervisor is None: 271 self.op.hypervisor = self.cfg.GetHypervisorType() 272 elif self.op.mode == constants.IALLOCATOR_MODE_RELOC: 273 (self.inst_uuid, self.op.name) = ExpandInstanceUuidAndName(self.cfg, None, 274 self.op.name) 275 self.relocate_from_node_uuids = \ 276 list(self.cfg.GetInstanceInfo(self.inst_uuid).secondary_nodes) 277 elif self.op.mode in (constants.IALLOCATOR_MODE_CHG_GROUP, 278 constants.IALLOCATOR_MODE_NODE_EVAC): 279 if not self.op.instances: 280 raise errors.OpPrereqError("Missing instances", errors.ECODE_INVAL) 281 (_, self.op.instances) = GetWantedInstances(self, self.op.instances) 282 else: 283 raise errors.OpPrereqError("Invalid test allocator mode '%s'" % 284 self.op.mode, errors.ECODE_INVAL) 285 286 if self.op.direction == constants.IALLOCATOR_DIR_OUT: 287 if self.op.iallocator is None: 288 raise errors.OpPrereqError("Missing allocator name", 289 errors.ECODE_INVAL)
290
291 - def Exec(self, feedback_fn):
292 """Run the allocator test. 293 294 """ 295 if self.op.mode == constants.IALLOCATOR_MODE_ALLOC: 296 req = iallocator.IAReqInstanceAlloc(name=self.op.name, 297 memory=self.op.memory, 298 disks=self.op.disks, 299 disk_template=self.op.disk_template, 300 os=self.op.os, 301 tags=self.op.tags, 302 nics=self.op.nics, 303 vcpus=self.op.vcpus, 304 spindle_use=self.op.spindle_use, 305 hypervisor=self.op.hypervisor, 306 node_whitelist=None) 307 elif self.op.mode == constants.IALLOCATOR_MODE_RELOC: 308 req = iallocator.IAReqRelocate( 309 inst_uuid=self.inst_uuid, 310 relocate_from_node_uuids=list(self.relocate_from_node_uuids)) 311 elif self.op.mode == constants.IALLOCATOR_MODE_CHG_GROUP: 312 req = iallocator.IAReqGroupChange(instances=self.op.instances, 313 target_groups=self.op.target_groups) 314 elif self.op.mode == constants.IALLOCATOR_MODE_NODE_EVAC: 315 req = iallocator.IAReqNodeEvac(instances=self.op.instances, 316 evac_mode=self.op.evac_mode) 317 elif self.op.mode == constants.IALLOCATOR_MODE_MULTI_ALLOC: 318 disk_template = self.op.disk_template 319 insts = [iallocator.IAReqInstanceAlloc(name="%s%s" % (self.op.name, idx), 320 memory=self.op.memory, 321 disks=self.op.disks, 322 disk_template=disk_template, 323 os=self.op.os, 324 tags=self.op.tags, 325 nics=self.op.nics, 326 vcpus=self.op.vcpus, 327 spindle_use=self.op.spindle_use, 328 hypervisor=self.op.hypervisor, 329 node_whitelist=None) 330 for idx in range(self.op.count)] 331 req = iallocator.IAReqMultiInstanceAlloc(instances=insts) 332 else: 333 raise errors.ProgrammerError("Uncatched mode %s in" 334 " LUTestAllocator.Exec", self.op.mode) 335 336 ial = iallocator.IAllocator(self.cfg, self.rpc, req) 337 if self.op.direction == constants.IALLOCATOR_DIR_IN: 338 result = ial.in_text 339 else: 340 ial.Run(self.op.iallocator, validate=False) 341 result = ial.out_text 342 return result
343