4 # Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 """Logical units dealing with instance operations (start/stop/...).
24 Those operations have in common that they affect the operating system in a
25 running instance directly.
31 from ganeti import constants
32 from ganeti import errors
33 from ganeti import hypervisor
34 from ganeti import locking
35 from ganeti import objects
36 from ganeti import utils
37 from ganeti.cmdlib.base import LogicalUnit, NoHooksLU
38 from ganeti.cmdlib.common import INSTANCE_ONLINE, INSTANCE_DOWN, \
39 CheckHVParams, CheckInstanceState, CheckNodeOnline, ExpandNodeName, \
40 GetUpdatedParams, CheckOSParams, ShareAll
41 from ganeti.cmdlib.instance_storage import StartInstanceDisks, \
43 from ganeti.cmdlib.instance_utils import BuildInstanceHookEnvByObject, \
44 CheckInstanceBridgesExist, CheckNodeFreeMemory, CheckNodeHasOS
47 class LUInstanceStartup(LogicalUnit):
48 """Starts an instance.
51 HPATH = "instance-start"
52 HTYPE = constants.HTYPE_INSTANCE
55 def CheckArguments(self):
58 # fill the beparams dict
59 objects.UpgradeBeParams(self.op.beparams)
60 utils.ForceDictType(self.op.beparams, constants.BES_PARAMETER_TYPES)
62 def ExpandNames(self):
63 self._ExpandAndLockInstance()
64 self.recalculate_locks[locking.LEVEL_NODE_RES] = constants.LOCKS_REPLACE
66 def DeclareLocks(self, level):
67 if level == locking.LEVEL_NODE_RES:
68 self._LockInstancesNodes(primary_only=True, level=locking.LEVEL_NODE_RES)
70 def BuildHooksEnv(self):
73 This runs on master, primary and secondary nodes of the instance.
77 "FORCE": self.op.force,
80 env.update(BuildInstanceHookEnvByObject(self, self.instance))
84 def BuildHooksNodes(self):
88 nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
91 def CheckPrereq(self):
92 """Check prerequisites.
94 This checks that the instance is in the cluster.
97 self.instance = instance = self.cfg.GetInstanceInfo(self.op.instance_name)
98 assert self.instance is not None, \
99 "Cannot retrieve locked instance %s" % self.op.instance_name
103 # check hypervisor parameter syntax (locally)
104 cluster = self.cfg.GetClusterInfo()
105 utils.ForceDictType(self.op.hvparams, constants.HVS_PARAMETER_TYPES)
106 filled_hvp = cluster.FillHV(instance)
107 filled_hvp.update(self.op.hvparams)
108 hv_type = hypervisor.GetHypervisorClass(instance.hypervisor)
109 hv_type.CheckParameterSyntax(filled_hvp)
110 CheckHVParams(self, instance.all_nodes, instance.hypervisor, filled_hvp)
112 CheckInstanceState(self, instance, INSTANCE_ONLINE)
114 self.primary_offline = self.cfg.GetNodeInfo(instance.primary_node).offline
116 if self.primary_offline and self.op.ignore_offline_nodes:
117 self.LogWarning("Ignoring offline primary node")
119 if self.op.hvparams or self.op.beparams:
120 self.LogWarning("Overridden parameters are ignored")
122 CheckNodeOnline(self, instance.primary_node)
124 bep = self.cfg.GetClusterInfo().FillBE(instance)
125 bep.update(self.op.beparams)
127 # check bridges existence
128 CheckInstanceBridgesExist(self, instance)
130 remote_info = self.rpc.call_instance_info(instance.primary_node,
133 remote_info.Raise("Error checking node %s" % instance.primary_node,
134 prereq=True, ecode=errors.ECODE_ENVIRON)
135 if not remote_info.payload: # not running already
137 self, instance.primary_node, "starting instance %s" % instance.name,
138 bep[constants.BE_MINMEM], instance.hypervisor,
139 self.cfg.GetClusterInfo().hvparams[instance.hypervisor])
141 def Exec(self, feedback_fn):
142 """Start the instance.
145 instance = self.instance
146 force = self.op.force
147 reason = self.op.reason
149 if not self.op.no_remember:
150 self.cfg.MarkInstanceUp(instance.name)
152 if self.primary_offline:
153 assert self.op.ignore_offline_nodes
154 self.LogInfo("Primary node offline, marked instance as started")
156 node_current = instance.primary_node
158 StartInstanceDisks(self, instance, force)
161 self.rpc.call_instance_start(node_current,
162 (instance, self.op.hvparams,
164 self.op.startup_paused, reason)
165 msg = result.fail_msg
167 ShutdownInstanceDisks(self, instance)
168 raise errors.OpExecError("Could not start instance: %s" % msg)
171 class LUInstanceShutdown(LogicalUnit):
172 """Shutdown an instance.
175 HPATH = "instance-stop"
176 HTYPE = constants.HTYPE_INSTANCE
179 def ExpandNames(self):
180 self._ExpandAndLockInstance()
182 def BuildHooksEnv(self):
185 This runs on master, primary and secondary nodes of the instance.
188 env = BuildInstanceHookEnvByObject(self, self.instance)
189 env["TIMEOUT"] = self.op.timeout
192 def BuildHooksNodes(self):
193 """Build hooks nodes.
196 nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
199 def CheckPrereq(self):
200 """Check prerequisites.
202 This checks that the instance is in the cluster.
205 self.instance = self.cfg.GetInstanceInfo(self.op.instance_name)
206 assert self.instance is not None, \
207 "Cannot retrieve locked instance %s" % self.op.instance_name
209 if not self.op.force:
210 CheckInstanceState(self, self.instance, INSTANCE_ONLINE)
212 self.LogWarning("Ignoring offline instance check")
214 self.primary_offline = \
215 self.cfg.GetNodeInfo(self.instance.primary_node).offline
217 if self.primary_offline and self.op.ignore_offline_nodes:
218 self.LogWarning("Ignoring offline primary node")
220 CheckNodeOnline(self, self.instance.primary_node)
222 def Exec(self, feedback_fn):
223 """Shutdown the instance.
226 instance = self.instance
227 node_current = instance.primary_node
228 timeout = self.op.timeout
229 reason = self.op.reason
231 # If the instance is offline we shouldn't mark it as down, as that
232 # resets the offline flag.
233 if not self.op.no_remember and instance.admin_state in INSTANCE_ONLINE:
234 self.cfg.MarkInstanceDown(instance.name)
236 if self.primary_offline:
237 assert self.op.ignore_offline_nodes
238 self.LogInfo("Primary node offline, marked instance as stopped")
240 result = self.rpc.call_instance_shutdown(node_current, instance, timeout,
242 msg = result.fail_msg
244 self.LogWarning("Could not shutdown instance: %s", msg)
246 ShutdownInstanceDisks(self, instance)
249 class LUInstanceReinstall(LogicalUnit):
250 """Reinstall an instance.
253 HPATH = "instance-reinstall"
254 HTYPE = constants.HTYPE_INSTANCE
257 def ExpandNames(self):
258 self._ExpandAndLockInstance()
260 def BuildHooksEnv(self):
263 This runs on master, primary and secondary nodes of the instance.
266 return BuildInstanceHookEnvByObject(self, self.instance)
268 def BuildHooksNodes(self):
269 """Build hooks nodes.
272 nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
275 def CheckPrereq(self):
276 """Check prerequisites.
278 This checks that the instance is in the cluster and is not running.
281 instance = self.cfg.GetInstanceInfo(self.op.instance_name)
282 assert instance is not None, \
283 "Cannot retrieve locked instance %s" % self.op.instance_name
284 CheckNodeOnline(self, instance.primary_node, "Instance primary node"
285 " offline, cannot reinstall")
287 if instance.disk_template == constants.DT_DISKLESS:
288 raise errors.OpPrereqError("Instance '%s' has no disks" %
289 self.op.instance_name,
291 CheckInstanceState(self, instance, INSTANCE_DOWN, msg="cannot reinstall")
293 if self.op.os_type is not None:
295 pnode = ExpandNodeName(self.cfg, instance.primary_node)
296 CheckNodeHasOS(self, pnode, self.op.os_type, self.op.force_variant)
297 instance_os = self.op.os_type
299 instance_os = instance.os
301 nodelist = list(instance.all_nodes)
304 i_osdict = GetUpdatedParams(instance.osparams, self.op.osparams)
305 CheckOSParams(self, True, nodelist, instance_os, i_osdict)
306 self.os_inst = i_osdict # the new dict (without defaults)
310 self.instance = instance
312 def Exec(self, feedback_fn):
313 """Reinstall the instance.
318 if self.op.os_type is not None:
319 feedback_fn("Changing OS to '%s'..." % self.op.os_type)
320 inst.os = self.op.os_type
321 # Write to configuration
322 self.cfg.Update(inst, feedback_fn)
324 StartInstanceDisks(self, inst, None)
326 feedback_fn("Running the instance OS create scripts...")
327 # FIXME: pass debug option from opcode to backend
328 result = self.rpc.call_instance_os_add(inst.primary_node,
329 (inst, self.os_inst), True,
331 result.Raise("Could not install OS for instance %s on node %s" %
332 (inst.name, inst.primary_node))
334 ShutdownInstanceDisks(self, inst)
337 class LUInstanceReboot(LogicalUnit):
338 """Reboot an instance.
341 HPATH = "instance-reboot"
342 HTYPE = constants.HTYPE_INSTANCE
345 def ExpandNames(self):
346 self._ExpandAndLockInstance()
348 def BuildHooksEnv(self):
351 This runs on master, primary and secondary nodes of the instance.
355 "IGNORE_SECONDARIES": self.op.ignore_secondaries,
356 "REBOOT_TYPE": self.op.reboot_type,
357 "SHUTDOWN_TIMEOUT": self.op.shutdown_timeout,
360 env.update(BuildInstanceHookEnvByObject(self, self.instance))
364 def BuildHooksNodes(self):
365 """Build hooks nodes.
368 nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
371 def CheckPrereq(self):
372 """Check prerequisites.
374 This checks that the instance is in the cluster.
377 self.instance = instance = self.cfg.GetInstanceInfo(self.op.instance_name)
378 assert self.instance is not None, \
379 "Cannot retrieve locked instance %s" % self.op.instance_name
380 CheckInstanceState(self, instance, INSTANCE_ONLINE)
381 CheckNodeOnline(self, instance.primary_node)
383 # check bridges existence
384 CheckInstanceBridgesExist(self, instance)
386 def Exec(self, feedback_fn):
387 """Reboot the instance.
390 instance = self.instance
391 ignore_secondaries = self.op.ignore_secondaries
392 reboot_type = self.op.reboot_type
393 reason = self.op.reason
395 remote_info = self.rpc.call_instance_info(instance.primary_node,
398 remote_info.Raise("Error checking node %s" % instance.primary_node)
399 instance_running = bool(remote_info.payload)
401 node_current = instance.primary_node
403 if instance_running and reboot_type in [constants.INSTANCE_REBOOT_SOFT,
404 constants.INSTANCE_REBOOT_HARD]:
405 for disk in instance.disks:
406 self.cfg.SetDiskID(disk, node_current)
407 result = self.rpc.call_instance_reboot(node_current, instance,
409 self.op.shutdown_timeout, reason)
410 result.Raise("Could not reboot instance")
413 result = self.rpc.call_instance_shutdown(node_current, instance,
414 self.op.shutdown_timeout,
416 result.Raise("Could not shutdown instance for full reboot")
417 ShutdownInstanceDisks(self, instance)
419 self.LogInfo("Instance %s was already stopped, starting now",
421 StartInstanceDisks(self, instance, ignore_secondaries)
422 result = self.rpc.call_instance_start(node_current,
423 (instance, None, None), False,
425 msg = result.fail_msg
427 ShutdownInstanceDisks(self, instance)
428 raise errors.OpExecError("Could not start instance for"
429 " full reboot: %s" % msg)
431 self.cfg.MarkInstanceUp(instance.name)
434 def GetInstanceConsole(cluster, instance):
435 """Returns console information for an instance.
437 @type cluster: L{objects.Cluster}
438 @type instance: L{objects.Instance}
442 hyper = hypervisor.GetHypervisorClass(instance.hypervisor)
443 # beparams and hvparams are passed separately, to avoid editing the
444 # instance and then saving the defaults in the instance itself.
445 hvparams = cluster.FillHV(instance)
446 beparams = cluster.FillBE(instance)
447 console = hyper.GetInstanceConsole(instance, hvparams, beparams)
449 assert console.instance == instance.name
450 assert console.Validate()
452 return console.ToDict()
455 class LUInstanceConsole(NoHooksLU):
456 """Connect to an instance's console.
458 This is somewhat special in that it returns the command line that
459 you need to run on the master node in order to connect to the
465 def ExpandNames(self):
466 self.share_locks = ShareAll()
467 self._ExpandAndLockInstance()
469 def CheckPrereq(self):
470 """Check prerequisites.
472 This checks that the instance is in the cluster.
475 self.instance = self.cfg.GetInstanceInfo(self.op.instance_name)
476 assert self.instance is not None, \
477 "Cannot retrieve locked instance %s" % self.op.instance_name
478 CheckNodeOnline(self, self.instance.primary_node)
480 def Exec(self, feedback_fn):
481 """Connect to the console of an instance
484 instance = self.instance
485 node = instance.primary_node
487 cluster_hvparams = self.cfg.GetClusterInfo().hvparams
488 node_insts = self.rpc.call_instance_list([node],
489 [instance.hypervisor],
490 cluster_hvparams)[node]
491 node_insts.Raise("Can't get node information from %s" % node)
493 if instance.name not in node_insts.payload:
494 if instance.admin_state == constants.ADMINST_UP:
495 state = constants.INSTST_ERRORDOWN
496 elif instance.admin_state == constants.ADMINST_DOWN:
497 state = constants.INSTST_ADMINDOWN
499 state = constants.INSTST_ADMINOFFLINE
500 raise errors.OpExecError("Instance %s is not running (state %s)" %
501 (instance.name, state))
503 logging.debug("Connecting to console of %s on %s", instance.name, node)
505 return GetInstanceConsole(self.cfg.GetClusterInfo(), instance)