Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / instance_operation.py @ 9808764a

History | View | Annotate | Download (17.3 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
    # Handle OS parameters
296
    self._MergeValidateOsParams(instance)
297

    
298
    self.instance = instance
299

    
300
  def _MergeValidateOsParams(self, instance):
301
    "Handle the OS parameter merging and validation for the target instance."
302

    
303
    if self.op.os_type is not None:
304
      # OS verification
305
      CheckNodeHasOS(self, instance.primary_node, self.op.os_type,
306
                     self.op.force_variant)
307
      instance_os = self.op.os_type
308
    else:
309
      instance_os = instance.os
310

    
311
    node_uuids = list(instance.all_nodes)
312

    
313
    self.op.osparams = self.op.osparams or {}
314
    self.op.osparams_private = self.op.osparams_private or {}
315
    self.op.osparams_secret = self.op.osparams_secret or {}
316

    
317
    # Handle the use of 'default' values.
318
    params_public = GetUpdatedParams(instance.osparams, self.op.osparams)
319
    params_private = GetUpdatedParams(instance.osparams_private,
320
                                        self.op.osparams_private)
321
    params_secret = self.op.osparams_secret
322

    
323
    cluster = self.cfg.GetClusterInfo()
324
    self.os_inst = cluster.SimpleFillOS(
325
      instance_os,
326
      params_public,
327
      os_params_private=params_private,
328
      os_params_secret=params_secret
329
    )
330

    
331
    CheckOSParams(self, True, node_uuids, instance_os, self.os_inst)
332

    
333
  def Exec(self, feedback_fn):
334
    """Reinstall the instance.
335

336
    """
337
    if self.op.os_type is not None:
338
      feedback_fn("Changing OS to '%s'..." % self.op.os_type)
339
      self.instance.os = self.op.os_type
340
      # Write to configuration
341
      self.cfg.Update(self.instance, feedback_fn)
342

    
343
    StartInstanceDisks(self, self.instance, None)
344
    try:
345
      feedback_fn("Running the instance OS create scripts...")
346
      # FIXME: pass debug option from opcode to backend
347
      result = self.rpc.call_instance_os_add(self.instance.primary_node,
348
                                             (self.instance, self.os_inst),
349
                                             True, self.op.debug_level)
350
      result.Raise("Could not install OS for instance %s on node %s" %
351
                   (self.instance.name,
352
                    self.cfg.GetNodeName(self.instance.primary_node)))
353
    finally:
354
      ShutdownInstanceDisks(self, self.instance)
355

    
356

    
357
class LUInstanceReboot(LogicalUnit):
358
  """Reboot an instance.
359

360
  """
361
  HPATH = "instance-reboot"
362
  HTYPE = constants.HTYPE_INSTANCE
363
  REQ_BGL = False
364

    
365
  def ExpandNames(self):
366
    self._ExpandAndLockInstance()
367

    
368
  def BuildHooksEnv(self):
369
    """Build hooks env.
370

371
    This runs on master, primary and secondary nodes of the instance.
372

373
    """
374
    env = {
375
      "IGNORE_SECONDARIES": self.op.ignore_secondaries,
376
      "REBOOT_TYPE": self.op.reboot_type,
377
      "SHUTDOWN_TIMEOUT": self.op.shutdown_timeout,
378
      }
379

    
380
    env.update(BuildInstanceHookEnvByObject(self, self.instance))
381

    
382
    return env
383

    
384
  def BuildHooksNodes(self):
385
    """Build hooks nodes.
386

387
    """
388
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
389
    return (nl, nl)
390

    
391
  def CheckPrereq(self):
392
    """Check prerequisites.
393

394
    This checks that the instance is in the cluster.
395

396
    """
397
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_uuid)
398
    assert self.instance is not None, \
399
      "Cannot retrieve locked instance %s" % self.op.instance_name
400
    CheckInstanceState(self, self.instance, INSTANCE_ONLINE)
401
    CheckNodeOnline(self, self.instance.primary_node)
402

    
403
    # check bridges existence
404
    CheckInstanceBridgesExist(self, self.instance)
405

    
406
  def Exec(self, feedback_fn):
407
    """Reboot the instance.
408

409
    """
410
    cluster = self.cfg.GetClusterInfo()
411
    remote_info = self.rpc.call_instance_info(
412
        self.instance.primary_node, self.instance.name,
413
        self.instance.hypervisor, cluster.hvparams[self.instance.hypervisor])
414
    remote_info.Raise("Error checking node %s" %
415
                      self.cfg.GetNodeName(self.instance.primary_node))
416
    instance_running = bool(remote_info.payload)
417

    
418
    current_node_uuid = self.instance.primary_node
419

    
420
    if instance_running and \
421
        self.op.reboot_type in [constants.INSTANCE_REBOOT_SOFT,
422
                                constants.INSTANCE_REBOOT_HARD]:
423
      result = self.rpc.call_instance_reboot(current_node_uuid, self.instance,
424
                                             self.op.reboot_type,
425
                                             self.op.shutdown_timeout,
426
                                             self.op.reason)
427
      result.Raise("Could not reboot instance")
428
    else:
429
      if instance_running:
430
        result = self.rpc.call_instance_shutdown(current_node_uuid,
431
                                                 self.instance,
432
                                                 self.op.shutdown_timeout,
433
                                                 self.op.reason)
434
        result.Raise("Could not shutdown instance for full reboot")
435
        ShutdownInstanceDisks(self, self.instance)
436
      else:
437
        self.LogInfo("Instance %s was already stopped, starting now",
438
                     self.instance.name)
439
      StartInstanceDisks(self, self.instance, self.op.ignore_secondaries)
440
      result = self.rpc.call_instance_start(current_node_uuid,
441
                                            (self.instance, None, None), False,
442
                                            self.op.reason)
443
      msg = result.fail_msg
444
      if msg:
445
        ShutdownInstanceDisks(self, self.instance)
446
        raise errors.OpExecError("Could not start instance for"
447
                                 " full reboot: %s" % msg)
448

    
449
    self.cfg.MarkInstanceUp(self.instance.uuid)
450

    
451

    
452
def GetInstanceConsole(cluster, instance, primary_node, node_group):
453
  """Returns console information for an instance.
454

455
  @type cluster: L{objects.Cluster}
456
  @type instance: L{objects.Instance}
457
  @type primary_node: L{objects.Node}
458
  @type node_group: L{objects.NodeGroup}
459
  @rtype: dict
460

461
  """
462
  hyper = hypervisor.GetHypervisorClass(instance.hypervisor)
463
  # beparams and hvparams are passed separately, to avoid editing the
464
  # instance and then saving the defaults in the instance itself.
465
  hvparams = cluster.FillHV(instance)
466
  beparams = cluster.FillBE(instance)
467
  console = hyper.GetInstanceConsole(instance, primary_node, node_group,
468
                                     hvparams, beparams)
469

    
470
  assert console.instance == instance.name
471
  assert console.Validate()
472

    
473
  return console.ToDict()
474

    
475

    
476
class LUInstanceConsole(NoHooksLU):
477
  """Connect to an instance's console.
478

479
  This is somewhat special in that it returns the command line that
480
  you need to run on the master node in order to connect to the
481
  console.
482

483
  """
484
  REQ_BGL = False
485

    
486
  def ExpandNames(self):
487
    self.share_locks = ShareAll()
488
    self._ExpandAndLockInstance()
489

    
490
  def CheckPrereq(self):
491
    """Check prerequisites.
492

493
    This checks that the instance is in the cluster.
494

495
    """
496
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_uuid)
497
    assert self.instance is not None, \
498
      "Cannot retrieve locked instance %s" % self.op.instance_name
499
    CheckNodeOnline(self, self.instance.primary_node)
500

    
501
  def Exec(self, feedback_fn):
502
    """Connect to the console of an instance
503

504
    """
505
    node_uuid = self.instance.primary_node
506

    
507
    cluster_hvparams = self.cfg.GetClusterInfo().hvparams
508
    node_insts = self.rpc.call_instance_list(
509
                   [node_uuid], [self.instance.hypervisor],
510
                   cluster_hvparams)[node_uuid]
511
    node_insts.Raise("Can't get node information from %s" %
512
                     self.cfg.GetNodeName(node_uuid))
513

    
514
    if self.instance.name not in node_insts.payload:
515
      if self.instance.admin_state == constants.ADMINST_UP:
516
        state = constants.INSTST_ERRORDOWN
517
      elif self.instance.admin_state == constants.ADMINST_DOWN:
518
        state = constants.INSTST_ADMINDOWN
519
      else:
520
        state = constants.INSTST_ADMINOFFLINE
521
      raise errors.OpExecError("Instance %s is not running (state %s)" %
522
                               (self.instance.name, state))
523

    
524
    logging.debug("Connecting to console of %s on %s", self.instance.name,
525
                  self.cfg.GetNodeName(node_uuid))
526

    
527
    node = self.cfg.GetNodeInfo(self.instance.primary_node)
528
    group = self.cfg.GetNodeGroup(node.group)
529
    return GetInstanceConsole(self.cfg.GetClusterInfo(),
530
                              self.instance, node, group)