Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / instance_operation.py @ b54ecf12

History | View | Annotate | Download (15.7 kB)

1
#
2
#
3

    
4
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Google Inc.
5
#
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.
10
#
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.
15
#
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
19
# 02110-1301, USA.
20

    
21

    
22
"""Logical units dealing with instance operations (start/stop/...).
23

24
Those operations have in common that they affect the operating system in a
25
running instance directly.
26

27
"""
28

    
29
import logging
30

    
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
45

    
46

    
47
class LUInstanceStartup(LogicalUnit):
48
  """Starts an instance.
49

50
  """
51
  HPATH = "instance-start"
52
  HTYPE = constants.HTYPE_INSTANCE
53
  REQ_BGL = False
54

    
55
  def CheckArguments(self):
56
    # extra beparams
57
    if self.op.beparams:
58
      # fill the beparams dict
59
      objects.UpgradeBeParams(self.op.beparams)
60
      utils.ForceDictType(self.op.beparams, constants.BES_PARAMETER_TYPES)
61

    
62
  def ExpandNames(self):
63
    self._ExpandAndLockInstance()
64
    self.recalculate_locks[locking.LEVEL_NODE_RES] = constants.LOCKS_REPLACE
65

    
66
  def DeclareLocks(self, level):
67
    if level == locking.LEVEL_NODE_RES:
68
      self._LockInstancesNodes(primary_only=True, level=locking.LEVEL_NODE_RES)
69

    
70
  def BuildHooksEnv(self):
71
    """Build hooks env.
72

73
    This runs on master, primary and secondary nodes of the instance.
74

75
    """
76
    env = {
77
      "FORCE": self.op.force,
78
      }
79

    
80
    env.update(BuildInstanceHookEnvByObject(self, self.instance))
81

    
82
    return env
83

    
84
  def BuildHooksNodes(self):
85
    """Build hooks nodes.
86

87
    """
88
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
89
    return (nl, nl)
90

    
91
  def CheckPrereq(self):
92
    """Check prerequisites.
93

94
    This checks that the instance is in the cluster.
95

96
    """
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
100

    
101
    # extra hvparams
102
    if self.op.hvparams:
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)
111

    
112
    CheckInstanceState(self, instance, INSTANCE_ONLINE)
113

    
114
    self.primary_offline = self.cfg.GetNodeInfo(instance.primary_node).offline
115

    
116
    if self.primary_offline and self.op.ignore_offline_nodes:
117
      self.LogWarning("Ignoring offline primary node")
118

    
119
      if self.op.hvparams or self.op.beparams:
120
        self.LogWarning("Overridden parameters are ignored")
121
    else:
122
      CheckNodeOnline(self, instance.primary_node)
123

    
124
      bep = self.cfg.GetClusterInfo().FillBE(instance)
125
      bep.update(self.op.beparams)
126

    
127
      # check bridges existence
128
      CheckInstanceBridgesExist(self, instance)
129

    
130
      remote_info = self.rpc.call_instance_info(instance.primary_node,
131
                                                instance.name,
132
                                                instance.hypervisor)
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)
139

    
140
  def Exec(self, feedback_fn):
141
    """Start the instance.
142

143
    """
144
    instance = self.instance
145
    force = self.op.force
146
    reason = self.op.reason
147

    
148
    if not self.op.no_remember:
149
      self.cfg.MarkInstanceUp(instance.name)
150

    
151
    if self.primary_offline:
152
      assert self.op.ignore_offline_nodes
153
      self.LogInfo("Primary node offline, marked instance as started")
154
    else:
155
      node_current = instance.primary_node
156

    
157
      StartInstanceDisks(self, instance, force)
158

    
159
      result = \
160
        self.rpc.call_instance_start(node_current,
161
                                     (instance, self.op.hvparams,
162
                                      self.op.beparams),
163
                                     self.op.startup_paused, reason)
164
      msg = result.fail_msg
165
      if msg:
166
        ShutdownInstanceDisks(self, instance)
167
        raise errors.OpExecError("Could not start instance: %s" % msg)
168

    
169

    
170
class LUInstanceShutdown(LogicalUnit):
171
  """Shutdown an instance.
172

173
  """
174
  HPATH = "instance-stop"
175
  HTYPE = constants.HTYPE_INSTANCE
176
  REQ_BGL = False
177

    
178
  def ExpandNames(self):
179
    self._ExpandAndLockInstance()
180

    
181
  def BuildHooksEnv(self):
182
    """Build hooks env.
183

184
    This runs on master, primary and secondary nodes of the instance.
185

186
    """
187
    env = BuildInstanceHookEnvByObject(self, self.instance)
188
    env["TIMEOUT"] = self.op.timeout
189
    return env
190

    
191
  def BuildHooksNodes(self):
192
    """Build hooks nodes.
193

194
    """
195
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
196
    return (nl, nl)
197

    
198
  def CheckPrereq(self):
199
    """Check prerequisites.
200

201
    This checks that the instance is in the cluster.
202

203
    """
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
207

    
208
    if not self.op.force:
209
      CheckInstanceState(self, self.instance, INSTANCE_ONLINE)
210
    else:
211
      self.LogWarning("Ignoring offline instance check")
212

    
213
    self.primary_offline = \
214
      self.cfg.GetNodeInfo(self.instance.primary_node).offline
215

    
216
    if self.primary_offline and self.op.ignore_offline_nodes:
217
      self.LogWarning("Ignoring offline primary node")
218
    else:
219
      CheckNodeOnline(self, self.instance.primary_node)
220

    
221
  def Exec(self, feedback_fn):
222
    """Shutdown the instance.
223

224
    """
225
    instance = self.instance
226
    node_current = instance.primary_node
227
    timeout = self.op.timeout
228
    reason = self.op.reason
229

    
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)
234

    
235
    if self.primary_offline:
236
      assert self.op.ignore_offline_nodes
237
      self.LogInfo("Primary node offline, marked instance as stopped")
238
    else:
239
      result = self.rpc.call_instance_shutdown(node_current, instance, timeout,
240
                                               reason)
241
      msg = result.fail_msg
242
      if msg:
243
        self.LogWarning("Could not shutdown instance: %s", msg)
244

    
245
      ShutdownInstanceDisks(self, instance)
246

    
247

    
248
class LUInstanceReinstall(LogicalUnit):
249
  """Reinstall an instance.
250

251
  """
252
  HPATH = "instance-reinstall"
253
  HTYPE = constants.HTYPE_INSTANCE
254
  REQ_BGL = False
255

    
256
  def ExpandNames(self):
257
    self._ExpandAndLockInstance()
258

    
259
  def BuildHooksEnv(self):
260
    """Build hooks env.
261

262
    This runs on master, primary and secondary nodes of the instance.
263

264
    """
265
    return BuildInstanceHookEnvByObject(self, self.instance)
266

    
267
  def BuildHooksNodes(self):
268
    """Build hooks nodes.
269

270
    """
271
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
272
    return (nl, nl)
273

    
274
  def CheckPrereq(self):
275
    """Check prerequisites.
276

277
    This checks that the instance is in the cluster and is not running.
278

279
    """
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")
285

    
286
    if instance.disk_template == constants.DT_DISKLESS:
287
      raise errors.OpPrereqError("Instance '%s' has no disks" %
288
                                 self.op.instance_name,
289
                                 errors.ECODE_INVAL)
290
    CheckInstanceState(self, instance, INSTANCE_DOWN, msg="cannot reinstall")
291

    
292
    if self.op.os_type is not None:
293
      # OS verification
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
297
    else:
298
      instance_os = instance.os
299

    
300
    nodelist = list(instance.all_nodes)
301

    
302
    if self.op.osparams:
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)
306
    else:
307
      self.os_inst = None
308

    
309
    self.instance = instance
310

    
311
  def Exec(self, feedback_fn):
312
    """Reinstall the instance.
313

314
    """
315
    inst = self.instance
316

    
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)
322

    
323
    StartInstanceDisks(self, inst, None)
324
    try:
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,
329
                                             self.op.debug_level)
330
      result.Raise("Could not install OS for instance %s on node %s" %
331
                   (inst.name, inst.primary_node))
332
    finally:
333
      ShutdownInstanceDisks(self, inst)
334

    
335

    
336
class LUInstanceReboot(LogicalUnit):
337
  """Reboot an instance.
338

339
  """
340
  HPATH = "instance-reboot"
341
  HTYPE = constants.HTYPE_INSTANCE
342
  REQ_BGL = False
343

    
344
  def ExpandNames(self):
345
    self._ExpandAndLockInstance()
346

    
347
  def BuildHooksEnv(self):
348
    """Build hooks env.
349

350
    This runs on master, primary and secondary nodes of the instance.
351

352
    """
353
    env = {
354
      "IGNORE_SECONDARIES": self.op.ignore_secondaries,
355
      "REBOOT_TYPE": self.op.reboot_type,
356
      "SHUTDOWN_TIMEOUT": self.op.shutdown_timeout,
357
      }
358

    
359
    env.update(BuildInstanceHookEnvByObject(self, self.instance))
360

    
361
    return env
362

    
363
  def BuildHooksNodes(self):
364
    """Build hooks nodes.
365

366
    """
367
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
368
    return (nl, nl)
369

    
370
  def CheckPrereq(self):
371
    """Check prerequisites.
372

373
    This checks that the instance is in the cluster.
374

375
    """
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)
381

    
382
    # check bridges existence
383
    CheckInstanceBridgesExist(self, instance)
384

    
385
  def Exec(self, feedback_fn):
386
    """Reboot the instance.
387

388
    """
389
    instance = self.instance
390
    ignore_secondaries = self.op.ignore_secondaries
391
    reboot_type = self.op.reboot_type
392
    reason = self.op.reason
393

    
394
    remote_info = self.rpc.call_instance_info(instance.primary_node,
395
                                              instance.name,
396
                                              instance.hypervisor)
397
    remote_info.Raise("Error checking node %s" % instance.primary_node)
398
    instance_running = bool(remote_info.payload)
399

    
400
    node_current = instance.primary_node
401

    
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,
407
                                             reboot_type,
408
                                             self.op.shutdown_timeout, reason)
409
      result.Raise("Could not reboot instance")
410
    else:
411
      if instance_running:
412
        result = self.rpc.call_instance_shutdown(node_current, instance,
413
                                                 self.op.shutdown_timeout,
414
                                                 reason)
415
        result.Raise("Could not shutdown instance for full reboot")
416
        ShutdownInstanceDisks(self, instance)
417
      else:
418
        self.LogInfo("Instance %s was already stopped, starting now",
419
                     instance.name)
420
      StartInstanceDisks(self, instance, ignore_secondaries)
421
      result = self.rpc.call_instance_start(node_current,
422
                                            (instance, None, None), False,
423
                                            reason)
424
      msg = result.fail_msg
425
      if msg:
426
        ShutdownInstanceDisks(self, instance)
427
        raise errors.OpExecError("Could not start instance for"
428
                                 " full reboot: %s" % msg)
429

    
430
    self.cfg.MarkInstanceUp(instance.name)
431

    
432

    
433
def GetInstanceConsole(cluster, instance):
434
  """Returns console information for an instance.
435

436
  @type cluster: L{objects.Cluster}
437
  @type instance: L{objects.Instance}
438
  @rtype: dict
439

440
  """
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)
447

    
448
  assert console.instance == instance.name
449
  assert console.Validate()
450

    
451
  return console.ToDict()
452

    
453

    
454
class LUInstanceConsole(NoHooksLU):
455
  """Connect to an instance's console.
456

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
459
  console.
460

461
  """
462
  REQ_BGL = False
463

    
464
  def ExpandNames(self):
465
    self.share_locks = ShareAll()
466
    self._ExpandAndLockInstance()
467

    
468
  def CheckPrereq(self):
469
    """Check prerequisites.
470

471
    This checks that the instance is in the cluster.
472

473
    """
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)
478

    
479
  def Exec(self, feedback_fn):
480
    """Connect to the console of an instance
481

482
    """
483
    instance = self.instance
484
    node = instance.primary_node
485

    
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)
489

    
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
495
      else:
496
        state = constants.INSTST_ADMINOFFLINE
497
      raise errors.OpExecError("Instance %s is not running (state %s)" %
498
                               (instance.name, state))
499

    
500
    logging.debug("Connecting to console of %s on %s", instance.name, node)
501

    
502
    return GetInstanceConsole(self.cfg.GetClusterInfo(), instance)