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, \
42 _ShutdownInstanceDisks
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
136 _CheckNodeFreeMemory(self, instance.primary_node,
137 "starting instance %s" % instance.name,
138 bep[constants.BE_MINMEM], instance.hypervisor)
140 def Exec(self, feedback_fn):
141 """Start the instance.
144 instance = self.instance
145 force = self.op.force
146 reason = self.op.reason
148 if not self.op.no_remember:
149 self.cfg.MarkInstanceUp(instance.name)
151 if self.primary_offline:
152 assert self.op.ignore_offline_nodes
153 self.LogInfo("Primary node offline, marked instance as started")
155 node_current = instance.primary_node
157 _StartInstanceDisks(self, instance, force)
160 self.rpc.call_instance_start(node_current,
161 (instance, self.op.hvparams,
163 self.op.startup_paused, reason)
164 msg = result.fail_msg
166 _ShutdownInstanceDisks(self, instance)
167 raise errors.OpExecError("Could not start instance: %s" % msg)
170 class LUInstanceShutdown(LogicalUnit):
171 """Shutdown an instance.
174 HPATH = "instance-stop"
175 HTYPE = constants.HTYPE_INSTANCE
178 def ExpandNames(self):
179 self._ExpandAndLockInstance()
181 def BuildHooksEnv(self):
184 This runs on master, primary and secondary nodes of the instance.
187 env = _BuildInstanceHookEnvByObject(self, self.instance)
188 env["TIMEOUT"] = self.op.timeout
191 def BuildHooksNodes(self):
192 """Build hooks nodes.
195 nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
198 def CheckPrereq(self):
199 """Check prerequisites.
201 This checks that the instance is in the cluster.
204 self.instance = self.cfg.GetInstanceInfo(self.op.instance_name)
205 assert self.instance is not None, \
206 "Cannot retrieve locked instance %s" % self.op.instance_name
208 if not self.op.force:
209 _CheckInstanceState(self, self.instance, INSTANCE_ONLINE)
211 self.LogWarning("Ignoring offline instance check")
213 self.primary_offline = \
214 self.cfg.GetNodeInfo(self.instance.primary_node).offline
216 if self.primary_offline and self.op.ignore_offline_nodes:
217 self.LogWarning("Ignoring offline primary node")
219 _CheckNodeOnline(self, self.instance.primary_node)
221 def Exec(self, feedback_fn):
222 """Shutdown the instance.
225 instance = self.instance
226 node_current = instance.primary_node
227 timeout = self.op.timeout
228 reason = self.op.reason
230 # If the instance is offline we shouldn't mark it as down, as that
231 # resets the offline flag.
232 if not self.op.no_remember and instance.admin_state in INSTANCE_ONLINE:
233 self.cfg.MarkInstanceDown(instance.name)
235 if self.primary_offline:
236 assert self.op.ignore_offline_nodes
237 self.LogInfo("Primary node offline, marked instance as stopped")
239 result = self.rpc.call_instance_shutdown(node_current, instance, timeout,
241 msg = result.fail_msg
243 self.LogWarning("Could not shutdown instance: %s", msg)
245 _ShutdownInstanceDisks(self, instance)
248 class LUInstanceReinstall(LogicalUnit):
249 """Reinstall an instance.
252 HPATH = "instance-reinstall"
253 HTYPE = constants.HTYPE_INSTANCE
256 def ExpandNames(self):
257 self._ExpandAndLockInstance()
259 def BuildHooksEnv(self):
262 This runs on master, primary and secondary nodes of the instance.
265 return _BuildInstanceHookEnvByObject(self, self.instance)
267 def BuildHooksNodes(self):
268 """Build hooks nodes.
271 nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
274 def CheckPrereq(self):
275 """Check prerequisites.
277 This checks that the instance is in the cluster and is not running.
280 instance = self.cfg.GetInstanceInfo(self.op.instance_name)
281 assert instance is not None, \
282 "Cannot retrieve locked instance %s" % self.op.instance_name
283 _CheckNodeOnline(self, instance.primary_node, "Instance primary node"
284 " offline, cannot reinstall")
286 if instance.disk_template == constants.DT_DISKLESS:
287 raise errors.OpPrereqError("Instance '%s' has no disks" %
288 self.op.instance_name,
290 _CheckInstanceState(self, instance, INSTANCE_DOWN, msg="cannot reinstall")
292 if self.op.os_type is not None:
294 pnode = _ExpandNodeName(self.cfg, instance.primary_node)
295 _CheckNodeHasOS(self, pnode, self.op.os_type, self.op.force_variant)
296 instance_os = self.op.os_type
298 instance_os = instance.os
300 nodelist = list(instance.all_nodes)
303 i_osdict = _GetUpdatedParams(instance.osparams, self.op.osparams)
304 _CheckOSParams(self, True, nodelist, instance_os, i_osdict)
305 self.os_inst = i_osdict # the new dict (without defaults)
309 self.instance = instance
311 def Exec(self, feedback_fn):
312 """Reinstall the instance.
317 if self.op.os_type is not None:
318 feedback_fn("Changing OS to '%s'..." % self.op.os_type)
319 inst.os = self.op.os_type
320 # Write to configuration
321 self.cfg.Update(inst, feedback_fn)
323 _StartInstanceDisks(self, inst, None)
325 feedback_fn("Running the instance OS create scripts...")
326 # FIXME: pass debug option from opcode to backend
327 result = self.rpc.call_instance_os_add(inst.primary_node,
328 (inst, self.os_inst), True,
330 result.Raise("Could not install OS for instance %s on node %s" %
331 (inst.name, inst.primary_node))
333 _ShutdownInstanceDisks(self, inst)
336 class LUInstanceReboot(LogicalUnit):
337 """Reboot an instance.
340 HPATH = "instance-reboot"
341 HTYPE = constants.HTYPE_INSTANCE
344 def ExpandNames(self):
345 self._ExpandAndLockInstance()
347 def BuildHooksEnv(self):
350 This runs on master, primary and secondary nodes of the instance.
354 "IGNORE_SECONDARIES": self.op.ignore_secondaries,
355 "REBOOT_TYPE": self.op.reboot_type,
356 "SHUTDOWN_TIMEOUT": self.op.shutdown_timeout,
359 env.update(_BuildInstanceHookEnvByObject(self, self.instance))
363 def BuildHooksNodes(self):
364 """Build hooks nodes.
367 nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
370 def CheckPrereq(self):
371 """Check prerequisites.
373 This checks that the instance is in the cluster.
376 self.instance = instance = self.cfg.GetInstanceInfo(self.op.instance_name)
377 assert self.instance is not None, \
378 "Cannot retrieve locked instance %s" % self.op.instance_name
379 _CheckInstanceState(self, instance, INSTANCE_ONLINE)
380 _CheckNodeOnline(self, instance.primary_node)
382 # check bridges existence
383 _CheckInstanceBridgesExist(self, instance)
385 def Exec(self, feedback_fn):
386 """Reboot the instance.
389 instance = self.instance
390 ignore_secondaries = self.op.ignore_secondaries
391 reboot_type = self.op.reboot_type
392 reason = self.op.reason
394 remote_info = self.rpc.call_instance_info(instance.primary_node,
397 remote_info.Raise("Error checking node %s" % instance.primary_node)
398 instance_running = bool(remote_info.payload)
400 node_current = instance.primary_node
402 if instance_running and reboot_type in [constants.INSTANCE_REBOOT_SOFT,
403 constants.INSTANCE_REBOOT_HARD]:
404 for disk in instance.disks:
405 self.cfg.SetDiskID(disk, node_current)
406 result = self.rpc.call_instance_reboot(node_current, instance,
408 self.op.shutdown_timeout, reason)
409 result.Raise("Could not reboot instance")
412 result = self.rpc.call_instance_shutdown(node_current, instance,
413 self.op.shutdown_timeout,
415 result.Raise("Could not shutdown instance for full reboot")
416 _ShutdownInstanceDisks(self, instance)
418 self.LogInfo("Instance %s was already stopped, starting now",
420 _StartInstanceDisks(self, instance, ignore_secondaries)
421 result = self.rpc.call_instance_start(node_current,
422 (instance, None, None), False,
424 msg = result.fail_msg
426 _ShutdownInstanceDisks(self, instance)
427 raise errors.OpExecError("Could not start instance for"
428 " full reboot: %s" % msg)
430 self.cfg.MarkInstanceUp(instance.name)
433 def _GetInstanceConsole(cluster, instance):
434 """Returns console information for an instance.
436 @type cluster: L{objects.Cluster}
437 @type instance: L{objects.Instance}
441 hyper = hypervisor.GetHypervisorClass(instance.hypervisor)
442 # beparams and hvparams are passed separately, to avoid editing the
443 # instance and then saving the defaults in the instance itself.
444 hvparams = cluster.FillHV(instance)
445 beparams = cluster.FillBE(instance)
446 console = hyper.GetInstanceConsole(instance, hvparams, beparams)
448 assert console.instance == instance.name
449 assert console.Validate()
451 return console.ToDict()
454 class LUInstanceConsole(NoHooksLU):
455 """Connect to an instance's console.
457 This is somewhat special in that it returns the command line that
458 you need to run on the master node in order to connect to the
464 def ExpandNames(self):
465 self.share_locks = _ShareAll()
466 self._ExpandAndLockInstance()
468 def CheckPrereq(self):
469 """Check prerequisites.
471 This checks that the instance is in the cluster.
474 self.instance = self.cfg.GetInstanceInfo(self.op.instance_name)
475 assert self.instance is not None, \
476 "Cannot retrieve locked instance %s" % self.op.instance_name
477 _CheckNodeOnline(self, self.instance.primary_node)
479 def Exec(self, feedback_fn):
480 """Connect to the console of an instance
483 instance = self.instance
484 node = instance.primary_node
486 node_insts = self.rpc.call_instance_list([node],
487 [instance.hypervisor])[node]
488 node_insts.Raise("Can't get node information from %s" % node)
490 if instance.name not in node_insts.payload:
491 if instance.admin_state == constants.ADMINST_UP:
492 state = constants.INSTST_ERRORDOWN
493 elif instance.admin_state == constants.ADMINST_DOWN:
494 state = constants.INSTST_ADMINDOWN
496 state = constants.INSTST_ADMINOFFLINE
497 raise errors.OpExecError("Instance %s is not running (state %s)" %
498 (instance.name, state))
500 logging.debug("Connecting to console of %s on %s", instance.name, node)
502 return _GetInstanceConsole(self.cfg.GetClusterInfo(), instance)