Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / instance_operation.py @ b3724640

History | View | Annotate | Download (16.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, 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
from ganeti.hypervisor import hv_base
46

    
47

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

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

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

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

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

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

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

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

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

    
83
    return env
84

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

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

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

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

97
    """
98
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_uuid)
99
    assert self.instance is not None, \
100
      "Cannot retrieve locked instance %s" % self.op.instance_name
101

    
102
    cluster = self.cfg.GetClusterInfo()
103
    # extra hvparams
104
    if self.op.hvparams:
105
      # check hypervisor parameter syntax (locally)
106
      utils.ForceDictType(self.op.hvparams, constants.HVS_PARAMETER_TYPES)
107
      filled_hvp = cluster.FillHV(self.instance)
108
      filled_hvp.update(self.op.hvparams)
109
      hv_type = hypervisor.GetHypervisorClass(self.instance.hypervisor)
110
      hv_type.CheckParameterSyntax(filled_hvp)
111
      CheckHVParams(self, self.instance.all_nodes, self.instance.hypervisor,
112
                    filled_hvp)
113

    
114
    CheckInstanceState(self, self.instance, INSTANCE_ONLINE)
115

    
116
    self.primary_offline = \
117
      self.cfg.GetNodeInfo(self.instance.primary_node).offline
118

    
119
    if self.primary_offline and self.op.ignore_offline_nodes:
120
      self.LogWarning("Ignoring offline primary node")
121

    
122
      if self.op.hvparams or self.op.beparams:
123
        self.LogWarning("Overridden parameters are ignored")
124
    else:
125
      CheckNodeOnline(self, self.instance.primary_node)
126

    
127
      bep = self.cfg.GetClusterInfo().FillBE(self.instance)
128
      bep.update(self.op.beparams)
129

    
130
      # check bridges existence
131
      CheckInstanceBridgesExist(self, self.instance)
132

    
133
      remote_info = self.rpc.call_instance_info(
134
          self.instance.primary_node, self.instance.name,
135
          self.instance.hypervisor, cluster.hvparams[self.instance.hypervisor])
136
      remote_info.Raise("Error checking node %s" %
137
                        self.cfg.GetNodeName(self.instance.primary_node),
138
                        prereq=True, ecode=errors.ECODE_ENVIRON)
139
      if remote_info.payload:
140
        if hv_base.HvInstanceState.IsShutdown(remote_info.payload["state"]):
141
          raise errors.OpPrereqError("Instance '%s' was shutdown by the user,"
142
                                     " please shutdown the instance before"
143
                                     " starting it again" % self.instance.name,
144
                                     errors.ECODE_INVAL)
145
      else: # not running already
146
        CheckNodeFreeMemory(
147
            self, self.instance.primary_node,
148
            "starting instance %s" % self.instance.name,
149
            bep[constants.BE_MINMEM], self.instance.hypervisor,
150
            self.cfg.GetClusterInfo().hvparams[self.instance.hypervisor])
151

    
152
  def Exec(self, feedback_fn):
153
    """Start the instance.
154

155
    """
156
    if not self.op.no_remember:
157
      self.cfg.MarkInstanceUp(self.instance.uuid)
158

    
159
    if self.primary_offline:
160
      assert self.op.ignore_offline_nodes
161
      self.LogInfo("Primary node offline, marked instance as started")
162
    else:
163
      StartInstanceDisks(self, self.instance, self.op.force)
164

    
165
      result = \
166
        self.rpc.call_instance_start(self.instance.primary_node,
167
                                     (self.instance, self.op.hvparams,
168
                                      self.op.beparams),
169
                                     self.op.startup_paused, self.op.reason)
170
      msg = result.fail_msg
171
      if msg:
172
        ShutdownInstanceDisks(self, self.instance)
173
        raise errors.OpExecError("Could not start instance: %s" % msg)
174

    
175

    
176
class LUInstanceShutdown(LogicalUnit):
177
  """Shutdown an instance.
178

179
  """
180
  HPATH = "instance-stop"
181
  HTYPE = constants.HTYPE_INSTANCE
182
  REQ_BGL = False
183

    
184
  def ExpandNames(self):
185
    self._ExpandAndLockInstance()
186

    
187
  def BuildHooksEnv(self):
188
    """Build hooks env.
189

190
    This runs on master, primary and secondary nodes of the instance.
191

192
    """
193
    env = BuildInstanceHookEnvByObject(self, self.instance)
194
    env["TIMEOUT"] = self.op.timeout
195
    return env
196

    
197
  def BuildHooksNodes(self):
198
    """Build hooks nodes.
199

200
    """
201
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
202
    return (nl, nl)
203

    
204
  def CheckPrereq(self):
205
    """Check prerequisites.
206

207
    This checks that the instance is in the cluster.
208

209
    """
210
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_uuid)
211
    assert self.instance is not None, \
212
      "Cannot retrieve locked instance %s" % self.op.instance_name
213

    
214
    if not self.op.force:
215
      CheckInstanceState(self, self.instance, INSTANCE_ONLINE)
216
    else:
217
      self.LogWarning("Ignoring offline instance check")
218

    
219
    self.primary_offline = \
220
      self.cfg.GetNodeInfo(self.instance.primary_node).offline
221

    
222
    if self.primary_offline and self.op.ignore_offline_nodes:
223
      self.LogWarning("Ignoring offline primary node")
224
    else:
225
      CheckNodeOnline(self, self.instance.primary_node)
226

    
227
  def Exec(self, feedback_fn):
228
    """Shutdown the instance.
229

230
    """
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 self.instance.admin_state in INSTANCE_ONLINE:
234
      self.cfg.MarkInstanceDown(self.instance.uuid)
235

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

    
248
      ShutdownInstanceDisks(self, self.instance)
249

    
250

    
251
class LUInstanceReinstall(LogicalUnit):
252
  """Reinstall an instance.
253

254
  """
255
  HPATH = "instance-reinstall"
256
  HTYPE = constants.HTYPE_INSTANCE
257
  REQ_BGL = False
258

    
259
  def ExpandNames(self):
260
    self._ExpandAndLockInstance()
261

    
262
  def BuildHooksEnv(self):
263
    """Build hooks env.
264

265
    This runs on master, primary and secondary nodes of the instance.
266

267
    """
268
    return BuildInstanceHookEnvByObject(self, self.instance)
269

    
270
  def BuildHooksNodes(self):
271
    """Build hooks nodes.
272

273
    """
274
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
275
    return (nl, nl)
276

    
277
  def CheckPrereq(self):
278
    """Check prerequisites.
279

280
    This checks that the instance is in the cluster and is not running.
281

282
    """
283
    instance = self.cfg.GetInstanceInfo(self.op.instance_uuid)
284
    assert instance is not None, \
285
      "Cannot retrieve locked instance %s" % self.op.instance_name
286
    CheckNodeOnline(self, instance.primary_node, "Instance primary node"
287
                    " offline, cannot reinstall")
288

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

    
295
    if self.op.os_type is not None:
296
      # OS verification
297
      CheckNodeHasOS(self, instance.primary_node, self.op.os_type,
298
                     self.op.force_variant)
299
      instance_os = self.op.os_type
300
    else:
301
      instance_os = instance.os
302

    
303
    node_uuids = list(instance.all_nodes)
304

    
305
    if self.op.osparams:
306
      i_osdict = GetUpdatedParams(instance.osparams, self.op.osparams)
307
      CheckOSParams(self, True, node_uuids, instance_os, i_osdict)
308
      self.os_inst = i_osdict # the new dict (without defaults)
309
    else:
310
      self.os_inst = None
311

    
312
    self.instance = instance
313

    
314
  def Exec(self, feedback_fn):
315
    """Reinstall the instance.
316

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

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

    
337

    
338
class LUInstanceReboot(LogicalUnit):
339
  """Reboot an instance.
340

341
  """
342
  HPATH = "instance-reboot"
343
  HTYPE = constants.HTYPE_INSTANCE
344
  REQ_BGL = False
345

    
346
  def ExpandNames(self):
347
    self._ExpandAndLockInstance()
348

    
349
  def BuildHooksEnv(self):
350
    """Build hooks env.
351

352
    This runs on master, primary and secondary nodes of the instance.
353

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

    
361
    env.update(BuildInstanceHookEnvByObject(self, self.instance))
362

    
363
    return env
364

    
365
  def BuildHooksNodes(self):
366
    """Build hooks nodes.
367

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

    
372
  def CheckPrereq(self):
373
    """Check prerequisites.
374

375
    This checks that the instance is in the cluster.
376

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

    
384
    # check bridges existence
385
    CheckInstanceBridgesExist(self, self.instance)
386

    
387
  def Exec(self, feedback_fn):
388
    """Reboot the instance.
389

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

    
399
    current_node_uuid = self.instance.primary_node
400

    
401
    if instance_running and \
402
        self.op.reboot_type in [constants.INSTANCE_REBOOT_SOFT,
403
                                constants.INSTANCE_REBOOT_HARD]:
404
      result = self.rpc.call_instance_reboot(current_node_uuid, self.instance,
405
                                             self.op.reboot_type,
406
                                             self.op.shutdown_timeout,
407
                                             self.op.reason)
408
      result.Raise("Could not reboot instance")
409
    else:
410
      if instance_running:
411
        result = self.rpc.call_instance_shutdown(current_node_uuid,
412
                                                 self.instance,
413
                                                 self.op.shutdown_timeout,
414
                                                 self.op.reason)
415
        result.Raise("Could not shutdown instance for full reboot")
416
        ShutdownInstanceDisks(self, self.instance)
417
      else:
418
        self.LogInfo("Instance %s was already stopped, starting now",
419
                     self.instance.name)
420
      StartInstanceDisks(self, self.instance, self.op.ignore_secondaries)
421
      result = self.rpc.call_instance_start(current_node_uuid,
422
                                            (self.instance, None, None), False,
423
                                            self.op.reason)
424
      msg = result.fail_msg
425
      if msg:
426
        ShutdownInstanceDisks(self, self.instance)
427
        raise errors.OpExecError("Could not start instance for"
428
                                 " full reboot: %s" % msg)
429

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

    
432

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

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

442
  """
443
  hyper = hypervisor.GetHypervisorClass(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, node_group,
449
                                     hvparams, beparams)
450

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

    
454
  return console.ToDict()
455

    
456

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

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

464
  """
465
  REQ_BGL = False
466

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

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

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

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

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

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

    
488
    cluster_hvparams = self.cfg.GetClusterInfo().hvparams
489
    node_insts = self.rpc.call_instance_list(
490
                   [node_uuid], [self.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 self.instance.name not in node_insts.payload:
496
      if self.instance.admin_state == constants.ADMINST_UP:
497
        state = constants.INSTST_ERRORDOWN
498
      elif self.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
                               (self.instance.name, state))
504

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

    
508
    node = self.cfg.GetNodeInfo(self.instance.primary_node)
509
    group = self.cfg.GetNodeGroup(node.group)
510
    return GetInstanceConsole(self.cfg.GetClusterInfo(),
511
                              self.instance, node, group)