Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / instance_operation.py @ 1c3231aa

History | View | Annotate | Download (16.1 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, GetUpdatedParams, \
40
  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
    cluster = self.cfg.GetClusterInfo()
102
    # extra hvparams
103
    if self.op.hvparams:
104
      # check hypervisor parameter syntax (locally)
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(
131
          instance.primary_node, instance.name, instance.hypervisor,
132
          cluster.hvparams[instance.hypervisor])
133
      remote_info.Raise("Error checking node %s" %
134
                        self.cfg.GetNodeName(instance.primary_node),
135
                        prereq=True, ecode=errors.ECODE_ENVIRON)
136
      if not remote_info.payload: # not running already
137
        CheckNodeFreeMemory(
138
            self, instance.primary_node, "starting instance %s" % instance.name,
139
            bep[constants.BE_MINMEM], instance.hypervisor,
140
            self.cfg.GetClusterInfo().hvparams[instance.hypervisor])
141

    
142
  def Exec(self, feedback_fn):
143
    """Start the instance.
144

145
    """
146
    instance = self.instance
147
    force = self.op.force
148
    reason = self.op.reason
149

    
150
    if not self.op.no_remember:
151
      self.cfg.MarkInstanceUp(instance.name)
152

    
153
    if self.primary_offline:
154
      assert self.op.ignore_offline_nodes
155
      self.LogInfo("Primary node offline, marked instance as started")
156
    else:
157
      StartInstanceDisks(self, instance, force)
158

    
159
      result = \
160
        self.rpc.call_instance_start(instance.primary_node,
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
    timeout = self.op.timeout
227
    reason = self.op.reason
228

    
229
    # If the instance is offline we shouldn't mark it as down, as that
230
    # resets the offline flag.
231
    if not self.op.no_remember and instance.admin_state in INSTANCE_ONLINE:
232
      self.cfg.MarkInstanceDown(instance.name)
233

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

    
244
      ShutdownInstanceDisks(self, instance)
245

    
246

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

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

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

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

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

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

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

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

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

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

278
    """
279
    instance = self.cfg.GetInstanceInfo(self.op.instance_name)
280
    assert instance is not None, \
281
      "Cannot retrieve locked instance %s" % self.op.instance_name
282
    CheckNodeOnline(self, instance.primary_node, "Instance primary node"
283
                    " offline, cannot reinstall")
284

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

    
291
    if self.op.os_type is not None:
292
      # OS verification
293
      CheckNodeHasOS(self, instance.primary_node, self.op.os_type,
294
                     self.op.force_variant)
295
      instance_os = self.op.os_type
296
    else:
297
      instance_os = instance.os
298

    
299
    node_uuids = list(instance.all_nodes)
300

    
301
    if self.op.osparams:
302
      i_osdict = GetUpdatedParams(instance.osparams, self.op.osparams)
303
      CheckOSParams(self, True, node_uuids, instance_os, i_osdict)
304
      self.os_inst = i_osdict # the new dict (without defaults)
305
    else:
306
      self.os_inst = None
307

    
308
    self.instance = instance
309

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

313
    """
314
    inst = self.instance
315

    
316
    if self.op.os_type is not None:
317
      feedback_fn("Changing OS to '%s'..." % self.op.os_type)
318
      inst.os = self.op.os_type
319
      # Write to configuration
320
      self.cfg.Update(inst, feedback_fn)
321

    
322
    StartInstanceDisks(self, inst, None)
323
    try:
324
      feedback_fn("Running the instance OS create scripts...")
325
      # FIXME: pass debug option from opcode to backend
326
      result = self.rpc.call_instance_os_add(inst.primary_node,
327
                                             (inst, self.os_inst), True,
328
                                             self.op.debug_level)
329
      result.Raise("Could not install OS for instance %s on node %s" %
330
                   (inst.name, self.cfg.GetNodeName(inst.primary_node)))
331
    finally:
332
      ShutdownInstanceDisks(self, inst)
333

    
334

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

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

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

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

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

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

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

    
360
    return env
361

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

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

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

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

374
    """
375
    self.instance = instance = self.cfg.GetInstanceInfo(self.op.instance_name)
376
    assert self.instance is not None, \
377
      "Cannot retrieve locked instance %s" % self.op.instance_name
378
    CheckInstanceState(self, instance, INSTANCE_ONLINE)
379
    CheckNodeOnline(self, instance.primary_node)
380

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

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

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

    
393
    cluster = self.cfg.GetClusterInfo()
394
    remote_info = self.rpc.call_instance_info(
395
        instance.primary_node, instance.name, instance.hypervisor,
396
        cluster.hvparams[instance.hypervisor])
397
    remote_info.Raise("Error checking node %s" %
398
                      self.cfg.GetNodeName(instance.primary_node))
399
    instance_running = bool(remote_info.payload)
400

    
401
    current_node_uuid = instance.primary_node
402

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

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

    
433

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

437
  @type cluster: L{objects.Cluster}
438
  @type instance: L{objects.Instance}
439
  @type primary_node: L{objects.Node}
440
  @rtype: dict
441

442
  """
443
  hyper = hypervisor.GetHypervisor(instance.hypervisor)
444
  # beparams and hvparams are passed separately, to avoid editing the
445
  # instance and then saving the defaults in the instance itself.
446
  hvparams = cluster.FillHV(instance)
447
  beparams = cluster.FillBE(instance)
448
  console = hyper.GetInstanceConsole(instance, primary_node, hvparams, beparams)
449

    
450
  assert console.instance == instance.name
451
  assert console.Validate()
452

    
453
  return console.ToDict()
454

    
455

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

459
  This is somewhat special in that it returns the command line that
460
  you need to run on the master node in order to connect to the
461
  console.
462

463
  """
464
  REQ_BGL = False
465

    
466
  def ExpandNames(self):
467
    self.share_locks = ShareAll()
468
    self._ExpandAndLockInstance()
469

    
470
  def CheckPrereq(self):
471
    """Check prerequisites.
472

473
    This checks that the instance is in the cluster.
474

475
    """
476
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_name)
477
    assert self.instance is not None, \
478
      "Cannot retrieve locked instance %s" % self.op.instance_name
479
    CheckNodeOnline(self, self.instance.primary_node)
480

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

484
    """
485
    instance = self.instance
486
    node_uuid = instance.primary_node
487

    
488
    cluster_hvparams = self.cfg.GetClusterInfo().hvparams
489
    node_insts = self.rpc.call_instance_list([node_uuid],
490
                                             [instance.hypervisor],
491
                                             cluster_hvparams)[node_uuid]
492
    node_insts.Raise("Can't get node information from %s" %
493
                     self.cfg.GetNodeName(node_uuid))
494

    
495
    if instance.name not in node_insts.payload:
496
      if instance.admin_state == constants.ADMINST_UP:
497
        state = constants.INSTST_ERRORDOWN
498
      elif instance.admin_state == constants.ADMINST_DOWN:
499
        state = constants.INSTST_ADMINDOWN
500
      else:
501
        state = constants.INSTST_ADMINOFFLINE
502
      raise errors.OpExecError("Instance %s is not running (state %s)" %
503
                               (instance.name, state))
504

    
505
    logging.debug("Connecting to console of %s on %s", instance.name,
506
                  self.cfg.GetNodeName(node_uuid))
507

    
508
    return GetInstanceConsole(self.cfg.GetClusterInfo(), instance,
509
                              self.cfg.GetNodeInfo(instance.primary_node))