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, 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  """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, CheckOSImage, ShareAll 
 50  from ganeti.cmdlib.instance_storage import StartInstanceDisks, \ 
 51    ShutdownInstanceDisks, ImageDisks 
 52  from ganeti.cmdlib.instance_utils import BuildInstanceHookEnvByObject, \ 
 53    CheckInstanceBridgesExist, CheckNodeFreeMemory, UpdateMetadata 
 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()] + \ 108 list(self.cfg.GetInstanceNodes(self.instance.uuid)) 109 return (nl, nl)
110
111 - def CheckPrereq(self):
112 """Check prerequisites. 113 114 This checks that the instance is in the cluster. 115 116 """ 117 self.instance = self.cfg.GetInstanceInfo(self.op.instance_uuid) 118 assert self.instance is not None, \ 119 "Cannot retrieve locked instance %s" % self.op.instance_name 120 121 cluster = self.cfg.GetClusterInfo() 122 # extra hvparams 123 if self.op.hvparams: 124 # check hypervisor parameter syntax (locally) 125 utils.ForceDictType(self.op.hvparams, constants.HVS_PARAMETER_TYPES) 126 filled_hvp = cluster.FillHV(self.instance) 127 filled_hvp.update(self.op.hvparams) 128 hv_type = hypervisor.GetHypervisorClass(self.instance.hypervisor) 129 hv_type.CheckParameterSyntax(filled_hvp) 130 CheckHVParams(self, self.cfg.GetInstanceNodes(self.instance.uuid), 131 self.instance.hypervisor, filled_hvp) 132 133 CheckInstanceState(self, self.instance, INSTANCE_ONLINE) 134 135 self.primary_offline = \ 136 self.cfg.GetNodeInfo(self.instance.primary_node).offline 137 138 if self.primary_offline and self.op.ignore_offline_nodes: 139 self.LogWarning("Ignoring offline primary node") 140 141 if self.op.hvparams or self.op.beparams: 142 self.LogWarning("Overridden parameters are ignored") 143 else: 144 CheckNodeOnline(self, self.instance.primary_node) 145 146 bep = self.cfg.GetClusterInfo().FillBE(self.instance) 147 bep.update(self.op.beparams) 148 149 # check bridges existence 150 CheckInstanceBridgesExist(self, self.instance) 151 152 remote_info = self.rpc.call_instance_info( 153 self.instance.primary_node, self.instance.name, 154 self.instance.hypervisor, cluster.hvparams[self.instance.hypervisor]) 155 remote_info.Raise("Error checking node %s" % 156 self.cfg.GetNodeName(self.instance.primary_node), 157 prereq=True, ecode=errors.ECODE_ENVIRON) 158 159 self.requires_cleanup = False 160 161 if remote_info.payload: 162 if _IsInstanceUserDown(self.cfg.GetClusterInfo(), 163 self.instance, 164 remote_info.payload): 165 self.requires_cleanup = True 166 else: # not running already 167 CheckNodeFreeMemory( 168 self, self.instance.primary_node, 169 "starting instance %s" % self.instance.name, 170 bep[constants.BE_MINMEM], self.instance.hypervisor, 171 self.cfg.GetClusterInfo().hvparams[self.instance.hypervisor])
172
173 - def Exec(self, feedback_fn):
174 """Start the instance. 175 176 """ 177 if not self.op.no_remember: 178 self.instance = self.cfg.MarkInstanceUp(self.instance.uuid) 179 180 if self.primary_offline: 181 assert self.op.ignore_offline_nodes 182 self.LogInfo("Primary node offline, marked instance as started") 183 else: 184 if self.requires_cleanup: 185 result = self.rpc.call_instance_shutdown( 186 self.instance.primary_node, 187 self.instance, 188 self.op.shutdown_timeout, self.op.reason) 189 result.Raise("Could not shutdown instance '%s'" % self.instance.name) 190 191 ShutdownInstanceDisks(self, self.instance) 192 193 StartInstanceDisks(self, self.instance, self.op.force) 194 self.instance = self.cfg.GetInstanceInfo(self.instance.uuid) 195 196 result = \ 197 self.rpc.call_instance_start(self.instance.primary_node, 198 (self.instance, self.op.hvparams, 199 self.op.beparams), 200 self.op.startup_paused, self.op.reason) 201 if result.fail_msg: 202 ShutdownInstanceDisks(self, self.instance) 203 result.Raise("Could not start instance '%s'" % self.instance.name)
204 205
206 -class LUInstanceShutdown(LogicalUnit):
207 """Shutdown an instance. 208 209 """ 210 HPATH = "instance-stop" 211 HTYPE = constants.HTYPE_INSTANCE 212 REQ_BGL = False 213
214 - def ExpandNames(self):
216
217 - def CheckArguments(self):
218 """Check arguments. 219 220 """ 221 if self.op.no_remember and self.op.admin_state_source is not None: 222 self.LogWarning("Parameter 'admin_state_source' has no effect if used" 223 " with parameter 'no_remember'") 224 225 if self.op.admin_state_source is None: 226 self.op.admin_state_source = constants.ADMIN_SOURCE
227
228 - def BuildHooksEnv(self):
229 """Build hooks env. 230 231 This runs on master, primary and secondary nodes of the instance. 232 233 """ 234 env = BuildInstanceHookEnvByObject(self, self.instance) 235 env["TIMEOUT"] = self.op.timeout 236 return env
237
238 - def BuildHooksNodes(self):
239 """Build hooks nodes. 240 241 """ 242 nl = [self.cfg.GetMasterNode()] + \ 243 list(self.cfg.GetInstanceNodes(self.instance.uuid)) 244 return (nl, nl)
245
246 - def CheckPrereq(self):
247 """Check prerequisites. 248 249 This checks that the instance is in the cluster. 250 251 """ 252 self.instance = self.cfg.GetInstanceInfo(self.op.instance_uuid) 253 assert self.instance is not None, \ 254 "Cannot retrieve locked instance %s" % self.op.instance_name 255 256 if self.op.force: 257 self.LogWarning("Ignoring offline instance check") 258 else: 259 CheckInstanceState(self, self.instance, INSTANCE_ONLINE) 260 261 self.primary_offline = \ 262 self.cfg.GetNodeInfo(self.instance.primary_node).offline 263 264 if self.primary_offline and self.op.ignore_offline_nodes: 265 self.LogWarning("Ignoring offline primary node") 266 else: 267 CheckNodeOnline(self, self.instance.primary_node) 268 269 if self.op.admin_state_source == constants.USER_SOURCE: 270 cluster = self.cfg.GetClusterInfo() 271 272 result = self.rpc.call_instance_info( 273 self.instance.primary_node, 274 self.instance.name, 275 self.instance.hypervisor, 276 cluster.hvparams[self.instance.hypervisor]) 277 result.Raise("Error checking instance '%s'" % self.instance.name, 278 prereq=True) 279 280 if not _IsInstanceUserDown(cluster, 281 self.instance, 282 result.payload): 283 raise errors.OpPrereqError("Instance '%s' was not shutdown by the user" 284 % self.instance.name)
285
286 - def Exec(self, feedback_fn):
287 """Shutdown the instance. 288 289 """ 290 # If the instance is offline we shouldn't mark it as down, as that 291 # resets the offline flag. 292 if not self.op.no_remember and self.instance.admin_state in INSTANCE_ONLINE: 293 self.instance = self.cfg.MarkInstanceDown(self.instance.uuid) 294 295 if self.op.admin_state_source == constants.ADMIN_SOURCE: 296 self.cfg.MarkInstanceDown(self.instance.uuid) 297 elif self.op.admin_state_source == constants.USER_SOURCE: 298 self.cfg.MarkInstanceUserDown(self.instance.uuid) 299 300 if self.primary_offline: 301 assert self.op.ignore_offline_nodes 302 self.LogInfo("Primary node offline, marked instance as stopped") 303 else: 304 result = self.rpc.call_instance_shutdown( 305 self.instance.primary_node, 306 self.instance, 307 self.op.timeout, self.op.reason) 308 result.Raise("Could not shutdown instance '%s'" % self.instance.name) 309 310 ShutdownInstanceDisks(self, self.instance)
311 312
313 -class LUInstanceReinstall(LogicalUnit):
314 """Reinstall an instance. 315 316 """ 317 HPATH = "instance-reinstall" 318 HTYPE = constants.HTYPE_INSTANCE 319 REQ_BGL = False 320
321 - def CheckArguments(self):
322 CheckOSImage(self.op)
323
324 - def ExpandNames(self):
326
327 - def BuildHooksEnv(self):
328 """Build hooks env. 329 330 This runs on master, primary and secondary nodes of the instance. 331 332 """ 333 return BuildInstanceHookEnvByObject(self, self.instance)
334
335 - def BuildHooksNodes(self):
336 """Build hooks nodes. 337 338 """ 339 nl = [self.cfg.GetMasterNode()] + \ 340 list(self.cfg.GetInstanceNodes(self.instance.uuid)) 341 return (nl, nl)
342
343 - def CheckPrereq(self):
344 """Check prerequisites. 345 346 This checks that the instance is in the cluster and is not running. 347 348 """ 349 instance = self.cfg.GetInstanceInfo(self.op.instance_uuid) 350 assert instance is not None, \ 351 "Cannot retrieve locked instance %s" % self.op.instance_name 352 CheckNodeOnline(self, instance.primary_node, "Instance primary node" 353 " offline, cannot reinstall") 354 355 if not instance.disks: 356 raise errors.OpPrereqError("Instance '%s' has no disks" % 357 self.op.instance_name, 358 errors.ECODE_INVAL) 359 CheckInstanceState(self, instance, INSTANCE_DOWN, msg="cannot reinstall") 360 361 # Handle OS parameters 362 self._MergeValidateOsParams(instance) 363 364 self.instance = instance
365
366 - def _MergeValidateOsParams(self, instance):
367 "Handle the OS parameter merging and validation for the target instance." 368 node_uuids = list(self.cfg.GetInstanceNodes(instance.uuid)) 369 370 self.op.osparams = self.op.osparams or {} 371 self.op.osparams_private = self.op.osparams_private or {} 372 self.op.osparams_secret = self.op.osparams_secret or {} 373 374 # Handle the use of 'default' values. 375 params_public = GetUpdatedParams(instance.osparams, self.op.osparams) 376 params_private = GetUpdatedParams(instance.osparams_private, 377 self.op.osparams_private) 378 params_secret = self.op.osparams_secret 379 380 # Handle OS parameters 381 if self.op.os_type is not None: 382 instance_os = self.op.os_type 383 else: 384 instance_os = instance.os 385 386 cluster = self.cfg.GetClusterInfo() 387 self.osparams = cluster.SimpleFillOS( 388 instance_os, 389 params_public, 390 os_params_private=params_private, 391 os_params_secret=params_secret 392 ) 393 394 self.osparams_private = params_private 395 self.osparams_secret = params_secret 396 397 CheckOSParams(self, True, node_uuids, instance_os, self.osparams, 398 self.op.force_variant)
399
400 - def _ReinstallOSScripts(self, instance, osparams, debug_level):
401 """Reinstall OS scripts on an instance. 402 403 @type instance: L{objects.Instance} 404 @param instance: instance of which the OS scripts should run 405 406 @type osparams: L{dict} 407 @param osparams: OS parameters 408 409 @type debug_level: non-negative int 410 @param debug_level: debug level 411 412 @rtype: NoneType 413 @return: None 414 @raise errors.OpExecError: in case of failure 415 416 """ 417 self.LogInfo("Running instance OS create scripts...") 418 result = self.rpc.call_instance_os_add(instance.primary_node, 419 (instance, osparams), 420 True, 421 debug_level) 422 result.Raise("Could not install OS for instance '%s' on node '%s'" % 423 (instance.name, self.cfg.GetNodeName(instance.primary_node)))
424
425 - def Exec(self, feedback_fn):
426 """Reinstall the instance. 427 428 """ 429 os_image = objects.GetOSImage(self.op.osparams) 430 431 if os_image is not None: 432 feedback_fn("Using OS image '%s', not changing instance" 433 " configuration" % os_image) 434 else: 435 os_image = objects.GetOSImage(self.instance.osparams) 436 437 os_type = self.op.os_type 438 439 if os_type is not None: 440 feedback_fn("Changing OS scripts to '%s'..." % os_type) 441 self.instance.os = os_type 442 self.cfg.Update(self.instance, feedback_fn) 443 else: 444 os_type = self.instance.os 445 446 if not os_image and not os_type: 447 self.LogInfo("No OS scripts or OS image specified or found in the" 448 " instance's configuration, nothing to install") 449 else: 450 StartInstanceDisks(self, self.instance, None) 451 self.instance = self.cfg.GetInstanceInfo(self.instance.uuid) 452 try: 453 if os_image: 454 ImageDisks(self, self.instance, os_image) 455 456 if os_type: 457 self._ReinstallOSScripts(self.instance, self.osparams, 458 self.op.debug_level) 459 460 UpdateMetadata(feedback_fn, self.rpc, self.instance, 461 osparams_public=self.osparams, 462 osparams_private=self.osparams_private, 463 osparams_secret=self.osparams_secret) 464 finally: 465 ShutdownInstanceDisks(self, self.instance)
466 467
468 -class LUInstanceReboot(LogicalUnit):
469 """Reboot an instance. 470 471 """ 472 HPATH = "instance-reboot" 473 HTYPE = constants.HTYPE_INSTANCE 474 REQ_BGL = False 475
476 - def ExpandNames(self):
478
479 - def BuildHooksEnv(self):
480 """Build hooks env. 481 482 This runs on master, primary and secondary nodes of the instance. 483 484 """ 485 env = { 486 "IGNORE_SECONDARIES": self.op.ignore_secondaries, 487 "REBOOT_TYPE": self.op.reboot_type, 488 "SHUTDOWN_TIMEOUT": self.op.shutdown_timeout, 489 } 490 491 env.update(BuildInstanceHookEnvByObject(self, self.instance)) 492 493 return env
494
495 - def BuildHooksNodes(self):
496 """Build hooks nodes. 497 498 """ 499 nl = [self.cfg.GetMasterNode()] + \ 500 list(self.cfg.GetInstanceNodes(self.instance.uuid)) 501 return (nl, nl)
502
503 - def CheckPrereq(self):
504 """Check prerequisites. 505 506 This checks that the instance is in the cluster. 507 508 """ 509 self.instance = self.cfg.GetInstanceInfo(self.op.instance_uuid) 510 assert self.instance is not None, \ 511 "Cannot retrieve locked instance %s" % self.op.instance_name 512 CheckInstanceState(self, self.instance, INSTANCE_ONLINE) 513 CheckNodeOnline(self, self.instance.primary_node) 514 515 # check bridges existence 516 CheckInstanceBridgesExist(self, self.instance)
517
518 - def Exec(self, feedback_fn):
519 """Reboot the instance. 520 521 """ 522 cluster = self.cfg.GetClusterInfo() 523 remote_info = self.rpc.call_instance_info( 524 self.instance.primary_node, self.instance.name, 525 self.instance.hypervisor, cluster.hvparams[self.instance.hypervisor]) 526 remote_info.Raise("Error checking node %s" % 527 self.cfg.GetNodeName(self.instance.primary_node)) 528 instance_running = bool(remote_info.payload) 529 530 current_node_uuid = self.instance.primary_node 531 532 if instance_running and \ 533 self.op.reboot_type in [constants.INSTANCE_REBOOT_SOFT, 534 constants.INSTANCE_REBOOT_HARD]: 535 result = self.rpc.call_instance_reboot(current_node_uuid, self.instance, 536 self.op.reboot_type, 537 self.op.shutdown_timeout, 538 self.op.reason) 539 result.Raise("Could not reboot instance") 540 else: 541 if instance_running: 542 result = self.rpc.call_instance_shutdown(current_node_uuid, 543 self.instance, 544 self.op.shutdown_timeout, 545 self.op.reason) 546 result.Raise("Could not shutdown instance for full reboot") 547 ShutdownInstanceDisks(self, self.instance) 548 self.instance = self.cfg.GetInstanceInfo(self.instance.uuid) 549 else: 550 self.LogInfo("Instance %s was already stopped, starting now", 551 self.instance.name) 552 StartInstanceDisks(self, self.instance, self.op.ignore_secondaries) 553 self.instance = self.cfg.GetInstanceInfo(self.instance.uuid) 554 result = self.rpc.call_instance_start(current_node_uuid, 555 (self.instance, None, None), False, 556 self.op.reason) 557 msg = result.fail_msg 558 if msg: 559 ShutdownInstanceDisks(self, self.instance) 560 self.instance = self.cfg.GetInstanceInfo(self.instance.uuid) 561 raise errors.OpExecError("Could not start instance for" 562 " full reboot: %s" % msg) 563 564 self.cfg.MarkInstanceUp(self.instance.uuid)
565 566
567 -def GetInstanceConsole(cluster, instance, primary_node, node_group):
568 """Returns console information for an instance. 569 570 @type cluster: L{objects.Cluster} 571 @type instance: L{objects.Instance} 572 @type primary_node: L{objects.Node} 573 @type node_group: L{objects.NodeGroup} 574 @rtype: dict 575 576 """ 577 hyper = hypervisor.GetHypervisorClass(instance.hypervisor) 578 # beparams and hvparams are passed separately, to avoid editing the 579 # instance and then saving the defaults in the instance itself. 580 hvparams = cluster.FillHV(instance) 581 beparams = cluster.FillBE(instance) 582 console = hyper.GetInstanceConsole(instance, primary_node, node_group, 583 hvparams, beparams) 584 585 assert console.instance == instance.name 586 console.Validate() 587 588 return console.ToDict()
589 590
591 -class LUInstanceConsole(NoHooksLU):
592 """Connect to an instance's console. 593 594 This is somewhat special in that it returns the command line that 595 you need to run on the master node in order to connect to the 596 console. 597 598 """ 599 REQ_BGL = False 600
601 - def ExpandNames(self):
602 self.share_locks = ShareAll() 603 self._ExpandAndLockInstance()
604
605 - def CheckPrereq(self):
606 """Check prerequisites. 607 608 This checks that the instance is in the cluster. 609 610 """ 611 self.instance = self.cfg.GetInstanceInfo(self.op.instance_uuid) 612 assert self.instance is not None, \ 613 "Cannot retrieve locked instance %s" % self.op.instance_name 614 CheckNodeOnline(self, self.instance.primary_node)
615
616 - def Exec(self, feedback_fn):
617 """Connect to the console of an instance 618 619 """ 620 node_uuid = self.instance.primary_node 621 622 cluster_hvparams = self.cfg.GetClusterInfo().hvparams 623 node_insts = self.rpc.call_instance_list( 624 [node_uuid], [self.instance.hypervisor], 625 cluster_hvparams)[node_uuid] 626 node_insts.Raise("Can't get node information from %s" % 627 self.cfg.GetNodeName(node_uuid)) 628 629 if self.instance.name not in node_insts.payload: 630 if self.instance.admin_state == constants.ADMINST_UP: 631 state = constants.INSTST_ERRORDOWN 632 elif self.instance.admin_state == constants.ADMINST_DOWN: 633 state = constants.INSTST_ADMINDOWN 634 else: 635 state = constants.INSTST_ADMINOFFLINE 636 raise errors.OpExecError("Instance %s is not running (state %s)" % 637 (self.instance.name, state)) 638 639 logging.debug("Connecting to console of %s on %s", self.instance.name, 640 self.cfg.GetNodeName(node_uuid)) 641 642 node = self.cfg.GetNodeInfo(self.instance.primary_node) 643 group = self.cfg.GetNodeGroup(node.group) 644 return GetInstanceConsole(self.cfg.GetClusterInfo(), 645 self.instance, node, group)
646