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

Source Code for Module ganeti.cmdlib.instance_operation

  1  # 
  2  # 
  3   
  4  # Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 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  """Logical units dealing with instance operations (start/stop/...). 
 32   
 33  Those operations have in common that they affect the operating system in a 
 34  running instance directly. 
 35   
 36  """ 
 37   
 38  import logging 
 39   
 40  from ganeti import constants 
 41  from ganeti import errors 
 42  from ganeti import hypervisor 
 43  from ganeti import locking 
 44  from ganeti import objects 
 45  from ganeti import utils 
 46  from ganeti.cmdlib.base import LogicalUnit, NoHooksLU 
 47  from ganeti.cmdlib.common import INSTANCE_ONLINE, INSTANCE_DOWN, \ 
 48    CheckHVParams, CheckInstanceState, CheckNodeOnline, GetUpdatedParams, \ 
 49    CheckOSParams, ShareAll 
 50  from ganeti.cmdlib.instance_storage import StartInstanceDisks, \ 
 51    ShutdownInstanceDisks 
 52  from ganeti.cmdlib.instance_utils import BuildInstanceHookEnvByObject, \ 
 53    CheckInstanceBridgesExist, CheckNodeFreeMemory, CheckNodeHasOS 
 54  from ganeti.hypervisor import hv_base 
 55   
 56   
57 -def _IsInstanceUserDown(cluster, instance, instance_info):
58 hvparams = cluster.FillHV(instance, skip_globals=True) 59 return instance_info and \ 60 "state" in instance_info and \ 61 hv_base.HvInstanceState.IsShutdown(instance_info["state"]) and \ 62 (instance.hypervisor != constants.HT_KVM or 63 hvparams[constants.HV_KVM_USER_SHUTDOWN])
64 65
66 -class LUInstanceStartup(LogicalUnit):
67 """Starts an instance. 68 69 """ 70 HPATH = "instance-start" 71 HTYPE = constants.HTYPE_INSTANCE 72 REQ_BGL = False 73
74 - def CheckArguments(self):
75 # extra beparams 76 if self.op.beparams: 77 # fill the beparams dict 78 objects.UpgradeBeParams(self.op.beparams) 79 utils.ForceDictType(self.op.beparams, constants.BES_PARAMETER_TYPES)
80
81 - def ExpandNames(self):
82 self._ExpandAndLockInstance() 83 self.recalculate_locks[locking.LEVEL_NODE_RES] = constants.LOCKS_REPLACE
84
85 - def DeclareLocks(self, level):
86 if level == locking.LEVEL_NODE_RES: 87 self._LockInstancesNodes(primary_only=True, level=locking.LEVEL_NODE_RES)
88
89 - def BuildHooksEnv(self):
90 """Build hooks env. 91 92 This runs on master, primary and secondary nodes of the instance. 93 94 """ 95 env = { 96 "FORCE": self.op.force, 97 } 98 99 env.update(BuildInstanceHookEnvByObject(self, self.instance)) 100 101 return env
102
103 - def BuildHooksNodes(self):
104 """Build hooks nodes. 105 106 """ 107 nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes) 108 return (nl, nl)
109
110 - def CheckPrereq(self):
111 """Check prerequisites. 112 113 This checks that the instance is in the cluster. 114 115 """ 116 self.instance = self.cfg.GetInstanceInfo(self.op.instance_uuid) 117 assert self.instance is not None, \ 118 "Cannot retrieve locked instance %s" % self.op.instance_name 119 120 cluster = self.cfg.GetClusterInfo() 121 # extra hvparams 122 if self.op.hvparams: 123 # check hypervisor parameter syntax (locally) 124 utils.ForceDictType(self.op.hvparams, constants.HVS_PARAMETER_TYPES) 125 filled_hvp = cluster.FillHV(self.instance) 126 filled_hvp.update(self.op.hvparams) 127 hv_type = hypervisor.GetHypervisorClass(self.instance.hypervisor) 128 hv_type.CheckParameterSyntax(filled_hvp) 129 CheckHVParams(self, self.instance.all_nodes, self.instance.hypervisor, 130 filled_hvp) 131 132 CheckInstanceState(self, self.instance, INSTANCE_ONLINE) 133 134 self.primary_offline = \ 135 self.cfg.GetNodeInfo(self.instance.primary_node).offline 136 137 if self.primary_offline and self.op.ignore_offline_nodes: 138 self.LogWarning("Ignoring offline primary node") 139 140 if self.op.hvparams or self.op.beparams: 141 self.LogWarning("Overridden parameters are ignored") 142 else: 143 CheckNodeOnline(self, self.instance.primary_node) 144 145 bep = self.cfg.GetClusterInfo().FillBE(self.instance) 146 bep.update(self.op.beparams) 147 148 # check bridges existence 149 CheckInstanceBridgesExist(self, self.instance) 150 151 remote_info = self.rpc.call_instance_info( 152 self.instance.primary_node, self.instance.name, 153 self.instance.hypervisor, cluster.hvparams[self.instance.hypervisor]) 154 remote_info.Raise("Error checking node %s" % 155 self.cfg.GetNodeName(self.instance.primary_node), 156 prereq=True, ecode=errors.ECODE_ENVIRON) 157 158 self.requires_cleanup = False 159 160 if remote_info.payload: 161 if _IsInstanceUserDown(self.cfg.GetClusterInfo(), 162 self.instance, 163 remote_info.payload): 164 self.requires_cleanup = True 165 else: # not running already 166 CheckNodeFreeMemory( 167 self, self.instance.primary_node, 168 "starting instance %s" % self.instance.name, 169 bep[constants.BE_MINMEM], self.instance.hypervisor, 170 self.cfg.GetClusterInfo().hvparams[self.instance.hypervisor])
171
172 - def Exec(self, feedback_fn):
173 """Start the instance. 174 175 """ 176 if not self.op.no_remember: 177 self.cfg.MarkInstanceUp(self.instance.uuid) 178 179 if self.primary_offline: 180 assert self.op.ignore_offline_nodes 181 self.LogInfo("Primary node offline, marked instance as started") 182 else: 183 if self.requires_cleanup: 184 result = self.rpc.call_instance_shutdown( 185 self.instance.primary_node, 186 self.instance, 187 self.op.shutdown_timeout, self.op.reason) 188 result.Raise("Could not shutdown instance '%s'" % self.instance.name) 189 190 ShutdownInstanceDisks(self, self.instance) 191 192 StartInstanceDisks(self, self.instance, self.op.force) 193 194 result = \ 195 self.rpc.call_instance_start(self.instance.primary_node, 196 (self.instance, self.op.hvparams, 197 self.op.beparams), 198 self.op.startup_paused, self.op.reason) 199 if result.fail_msg: 200 ShutdownInstanceDisks(self, self.instance) 201 result.Raise("Could not start instance '%s'" % self.instance.name)
202 203
204 -class LUInstanceShutdown(LogicalUnit):
205 """Shutdown an instance. 206 207 """ 208 HPATH = "instance-stop" 209 HTYPE = constants.HTYPE_INSTANCE 210 REQ_BGL = False 211
212 - def ExpandNames(self):
214
215 - def CheckArguments(self):
216 """Check arguments. 217 218 """ 219 if self.op.no_remember and self.op.admin_state_source is not None: 220 self.LogWarning("Parameter 'admin_state_source' has no effect if used" 221 " with parameter 'no_remember'") 222 223 if self.op.admin_state_source is None: 224 self.op.admin_state_source = constants.ADMIN_SOURCE
225
226 - def BuildHooksEnv(self):
227 """Build hooks env. 228 229 This runs on master, primary and secondary nodes of the instance. 230 231 """ 232 env = BuildInstanceHookEnvByObject(self, self.instance) 233 env["TIMEOUT"] = self.op.timeout 234 return env
235
236 - def BuildHooksNodes(self):
237 """Build hooks nodes. 238 239 """ 240 nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes) 241 return (nl, nl)
242
243 - def CheckPrereq(self):
244 """Check prerequisites. 245 246 This checks that the instance is in the cluster. 247 248 """ 249 self.instance = self.cfg.GetInstanceInfo(self.op.instance_uuid) 250 assert self.instance is not None, \ 251 "Cannot retrieve locked instance %s" % self.op.instance_name 252 253 if self.op.force: 254 self.LogWarning("Ignoring offline instance check") 255 else: 256 CheckInstanceState(self, self.instance, INSTANCE_ONLINE) 257 258 self.primary_offline = \ 259 self.cfg.GetNodeInfo(self.instance.primary_node).offline 260 261 if self.primary_offline and self.op.ignore_offline_nodes: 262 self.LogWarning("Ignoring offline primary node") 263 else: 264 CheckNodeOnline(self, self.instance.primary_node) 265 266 if self.op.admin_state_source == constants.USER_SOURCE: 267 cluster = self.cfg.GetClusterInfo() 268 269 result = self.rpc.call_instance_info( 270 self.instance.primary_node, 271 self.instance.name, 272 self.instance.hypervisor, 273 cluster.hvparams[self.instance.hypervisor]) 274 result.Raise("Error checking instance '%s'" % self.instance.name, 275 prereq=True) 276 277 if not _IsInstanceUserDown(cluster, 278 self.instance, 279 result.payload): 280 raise errors.OpPrereqError("Instance '%s' was not shutdown by the user" 281 % self.instance.name)
282
283 - def Exec(self, feedback_fn):
284 """Shutdown the instance. 285 286 """ 287 # If the instance is offline we shouldn't mark it as down, as that 288 # resets the offline flag. 289 if not self.op.no_remember and self.instance.admin_state in INSTANCE_ONLINE: 290 if self.op.admin_state_source == constants.ADMIN_SOURCE: 291 self.cfg.MarkInstanceDown(self.instance.uuid) 292 elif self.op.admin_state_source == constants.USER_SOURCE: 293 self.cfg.MarkInstanceUserDown(self.instance.uuid) 294 295 if self.primary_offline: 296 assert self.op.ignore_offline_nodes 297 self.LogInfo("Primary node offline, marked instance as stopped") 298 else: 299 result = self.rpc.call_instance_shutdown( 300 self.instance.primary_node, 301 self.instance, 302 self.op.timeout, self.op.reason) 303 result.Raise("Could not shutdown instance '%s'" % self.instance.name) 304 305 ShutdownInstanceDisks(self, self.instance)
306 307
308 -class LUInstanceReinstall(LogicalUnit):
309 """Reinstall an instance. 310 311 """ 312 HPATH = "instance-reinstall" 313 HTYPE = constants.HTYPE_INSTANCE 314 REQ_BGL = False 315
316 - def ExpandNames(self):
318
319 - def BuildHooksEnv(self):
320 """Build hooks env. 321 322 This runs on master, primary and secondary nodes of the instance. 323 324 """ 325 return BuildInstanceHookEnvByObject(self, self.instance)
326
327 - def BuildHooksNodes(self):
328 """Build hooks nodes. 329 330 """ 331 nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes) 332 return (nl, nl)
333
334 - def CheckPrereq(self):
335 """Check prerequisites. 336 337 This checks that the instance is in the cluster and is not running. 338 339 """ 340 instance = self.cfg.GetInstanceInfo(self.op.instance_uuid) 341 assert instance is not None, \ 342 "Cannot retrieve locked instance %s" % self.op.instance_name 343 CheckNodeOnline(self, instance.primary_node, "Instance primary node" 344 " offline, cannot reinstall") 345 346 if instance.disk_template == constants.DT_DISKLESS: 347 raise errors.OpPrereqError("Instance '%s' has no disks" % 348 self.op.instance_name, 349 errors.ECODE_INVAL) 350 CheckInstanceState(self, instance, INSTANCE_DOWN, msg="cannot reinstall") 351 352 if self.op.os_type is not None: 353 # OS verification 354 CheckNodeHasOS(self, instance.primary_node, self.op.os_type, 355 self.op.force_variant) 356 instance_os = self.op.os_type 357 else: 358 instance_os = instance.os 359 360 node_uuids = list(instance.all_nodes) 361 362 if self.op.osparams: 363 i_osdict = GetUpdatedParams(instance.osparams, self.op.osparams) 364 CheckOSParams(self, True, node_uuids, instance_os, i_osdict) 365 self.os_inst = i_osdict # the new dict (without defaults) 366 else: 367 self.os_inst = None 368 369 self.instance = instance
370
371 - def Exec(self, feedback_fn):
372 """Reinstall the instance. 373 374 """ 375 if self.op.os_type is not None: 376 feedback_fn("Changing OS to '%s'..." % self.op.os_type) 377 self.instance.os = self.op.os_type 378 # Write to configuration 379 self.cfg.Update(self.instance, feedback_fn) 380 381 StartInstanceDisks(self, self.instance, None) 382 try: 383 feedback_fn("Running the instance OS create scripts...") 384 # FIXME: pass debug option from opcode to backend 385 result = self.rpc.call_instance_os_add(self.instance.primary_node, 386 (self.instance, self.os_inst), 387 True, self.op.debug_level) 388 result.Raise("Could not install OS for instance %s on node %s" % 389 (self.instance.name, 390 self.cfg.GetNodeName(self.instance.primary_node))) 391 finally: 392 ShutdownInstanceDisks(self, self.instance)
393 394
395 -class LUInstanceReboot(LogicalUnit):
396 """Reboot an instance. 397 398 """ 399 HPATH = "instance-reboot" 400 HTYPE = constants.HTYPE_INSTANCE 401 REQ_BGL = False 402
403 - def ExpandNames(self):
405
406 - def BuildHooksEnv(self):
407 """Build hooks env. 408 409 This runs on master, primary and secondary nodes of the instance. 410 411 """ 412 env = { 413 "IGNORE_SECONDARIES": self.op.ignore_secondaries, 414 "REBOOT_TYPE": self.op.reboot_type, 415 "SHUTDOWN_TIMEOUT": self.op.shutdown_timeout, 416 } 417 418 env.update(BuildInstanceHookEnvByObject(self, self.instance)) 419 420 return env
421
422 - def BuildHooksNodes(self):
423 """Build hooks nodes. 424 425 """ 426 nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes) 427 return (nl, nl)
428
429 - def CheckPrereq(self):
430 """Check prerequisites. 431 432 This checks that the instance is in the cluster. 433 434 """ 435 self.instance = self.cfg.GetInstanceInfo(self.op.instance_uuid) 436 assert self.instance is not None, \ 437 "Cannot retrieve locked instance %s" % self.op.instance_name 438 CheckInstanceState(self, self.instance, INSTANCE_ONLINE) 439 CheckNodeOnline(self, self.instance.primary_node) 440 441 # check bridges existence 442 CheckInstanceBridgesExist(self, self.instance)
443
444 - def Exec(self, feedback_fn):
445 """Reboot the instance. 446 447 """ 448 cluster = self.cfg.GetClusterInfo() 449 remote_info = self.rpc.call_instance_info( 450 self.instance.primary_node, self.instance.name, 451 self.instance.hypervisor, cluster.hvparams[self.instance.hypervisor]) 452 remote_info.Raise("Error checking node %s" % 453 self.cfg.GetNodeName(self.instance.primary_node)) 454 instance_running = bool(remote_info.payload) 455 456 current_node_uuid = self.instance.primary_node 457 458 if instance_running and \ 459 self.op.reboot_type in [constants.INSTANCE_REBOOT_SOFT, 460 constants.INSTANCE_REBOOT_HARD]: 461 result = self.rpc.call_instance_reboot(current_node_uuid, self.instance, 462 self.op.reboot_type, 463 self.op.shutdown_timeout, 464 self.op.reason) 465 result.Raise("Could not reboot instance") 466 else: 467 if instance_running: 468 result = self.rpc.call_instance_shutdown(current_node_uuid, 469 self.instance, 470 self.op.shutdown_timeout, 471 self.op.reason) 472 result.Raise("Could not shutdown instance for full reboot") 473 ShutdownInstanceDisks(self, self.instance) 474 else: 475 self.LogInfo("Instance %s was already stopped, starting now", 476 self.instance.name) 477 StartInstanceDisks(self, self.instance, self.op.ignore_secondaries) 478 result = self.rpc.call_instance_start(current_node_uuid, 479 (self.instance, None, None), False, 480 self.op.reason) 481 msg = result.fail_msg 482 if msg: 483 ShutdownInstanceDisks(self, self.instance) 484 raise errors.OpExecError("Could not start instance for" 485 " full reboot: %s" % msg) 486 487 self.cfg.MarkInstanceUp(self.instance.uuid)
488 489
490 -def GetInstanceConsole(cluster, instance, primary_node, node_group):
491 """Returns console information for an instance. 492 493 @type cluster: L{objects.Cluster} 494 @type instance: L{objects.Instance} 495 @type primary_node: L{objects.Node} 496 @type node_group: L{objects.NodeGroup} 497 @rtype: dict 498 499 """ 500 hyper = hypervisor.GetHypervisorClass(instance.hypervisor) 501 # beparams and hvparams are passed separately, to avoid editing the 502 # instance and then saving the defaults in the instance itself. 503 hvparams = cluster.FillHV(instance) 504 beparams = cluster.FillBE(instance) 505 console = hyper.GetInstanceConsole(instance, primary_node, node_group, 506 hvparams, beparams) 507 508 assert console.instance == instance.name 509 console.Validate() 510 511 return console.ToDict()
512 513
514 -class LUInstanceConsole(NoHooksLU):
515 """Connect to an instance's console. 516 517 This is somewhat special in that it returns the command line that 518 you need to run on the master node in order to connect to the 519 console. 520 521 """ 522 REQ_BGL = False 523
524 - def ExpandNames(self):
525 self.share_locks = ShareAll() 526 self._ExpandAndLockInstance()
527
528 - def CheckPrereq(self):
529 """Check prerequisites. 530 531 This checks that the instance is in the cluster. 532 533 """ 534 self.instance = self.cfg.GetInstanceInfo(self.op.instance_uuid) 535 assert self.instance is not None, \ 536 "Cannot retrieve locked instance %s" % self.op.instance_name 537 CheckNodeOnline(self, self.instance.primary_node)
538
539 - def Exec(self, feedback_fn):
540 """Connect to the console of an instance 541 542 """ 543 node_uuid = self.instance.primary_node 544 545 cluster_hvparams = self.cfg.GetClusterInfo().hvparams 546 node_insts = self.rpc.call_instance_list( 547 [node_uuid], [self.instance.hypervisor], 548 cluster_hvparams)[node_uuid] 549 node_insts.Raise("Can't get node information from %s" % 550 self.cfg.GetNodeName(node_uuid)) 551 552 if self.instance.name not in node_insts.payload: 553 if self.instance.admin_state == constants.ADMINST_UP: 554 state = constants.INSTST_ERRORDOWN 555 elif self.instance.admin_state == constants.ADMINST_DOWN: 556 state = constants.INSTST_ADMINDOWN 557 else: 558 state = constants.INSTST_ADMINOFFLINE 559 raise errors.OpExecError("Instance %s is not running (state %s)" % 560 (self.instance.name, state)) 561 562 logging.debug("Connecting to console of %s on %s", self.instance.name, 563 self.cfg.GetNodeName(node_uuid)) 564 565 node = self.cfg.GetNodeInfo(self.instance.primary_node) 566 group = self.cfg.GetNodeGroup(node.group) 567 return GetInstanceConsole(self.cfg.GetClusterInfo(), 568 self.instance, node, group)
569