Statistics
| Branch: | Tag: | Revision:

root / lib / cmdlib / instance_operation.py @ 34af39e8

History | View | Annotate | Download (16.2 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 = self.cfg.GetInstanceInfo(self.op.instance_uuid)
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(self.instance)
107
      filled_hvp.update(self.op.hvparams)
108
      hv_type = hypervisor.GetHypervisorClass(self.instance.hypervisor)
109
      hv_type.CheckParameterSyntax(filled_hvp)
110
      CheckHVParams(self, self.instance.all_nodes, self.instance.hypervisor,
111
                    filled_hvp)
112

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

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

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

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

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

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

    
132
      remote_info = self.rpc.call_instance_info(
133
          self.instance.primary_node, self.instance.name,
134
          self.instance.hypervisor, cluster.hvparams[self.instance.hypervisor])
135
      remote_info.Raise("Error checking node %s" %
136
                        self.cfg.GetNodeName(self.instance.primary_node),
137
                        prereq=True, ecode=errors.ECODE_ENVIRON)
138
      if not remote_info.payload: # not running already
139
        CheckNodeFreeMemory(
140
            self, self.instance.primary_node,
141
            "starting instance %s" % self.instance.name,
142
            bep[constants.BE_MINMEM], self.instance.hypervisor,
143
            self.cfg.GetClusterInfo().hvparams[self.instance.hypervisor])
144

    
145
  def Exec(self, feedback_fn):
146
    """Start the instance.
147

148
    """
149
    if not self.op.no_remember:
150
      self.cfg.MarkInstanceUp(self.instance.uuid)
151

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

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

    
168

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

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

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

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

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

185
    """
186
    env = BuildInstanceHookEnvByObject(self, self.instance)
187
    env["SHUTDOWN_TIMEOUT"] = self.op.shutdown_timeout
188
    return env
189

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

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

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

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

202
    """
203
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_uuid)
204
    assert self.instance is not None, \
205
      "Cannot retrieve locked instance %s" % self.op.instance_name
206

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

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

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

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

223
    """
224
    # If the instance is offline we shouldn't mark it as down, as that
225
    # resets the offline flag.
226
    if not self.op.no_remember and self.instance.admin_state in INSTANCE_ONLINE:
227
      self.cfg.MarkInstanceDown(self.instance.uuid)
228

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

    
241
      ShutdownInstanceDisks(self, self.instance)
242

    
243

    
244
class LUInstanceReinstall(LogicalUnit):
245
  """Reinstall an instance.
246

247
  """
248
  HPATH = "instance-reinstall"
249
  HTYPE = constants.HTYPE_INSTANCE
250
  REQ_BGL = False
251

    
252
  def ExpandNames(self):
253
    self._ExpandAndLockInstance()
254

    
255
  def BuildHooksEnv(self):
256
    """Build hooks env.
257

258
    This runs on master, primary and secondary nodes of the instance.
259

260
    """
261
    return BuildInstanceHookEnvByObject(self, self.instance)
262

    
263
  def BuildHooksNodes(self):
264
    """Build hooks nodes.
265

266
    """
267
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
268
    return (nl, nl)
269

    
270
  def CheckPrereq(self):
271
    """Check prerequisites.
272

273
    This checks that the instance is in the cluster and is not running.
274

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

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

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

    
296
    node_uuids = list(instance.all_nodes)
297

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

    
305
    self.instance = instance
306

    
307
  def Exec(self, feedback_fn):
308
    """Reinstall the instance.
309

310
    """
311
    if self.op.os_type is not None:
312
      feedback_fn("Changing OS to '%s'..." % self.op.os_type)
313
      self.instance.os = self.op.os_type
314
      # Write to configuration
315
      self.cfg.Update(self.instance, feedback_fn)
316

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

    
330

    
331
class LUInstanceReboot(LogicalUnit):
332
  """Reboot an instance.
333

334
  """
335
  HPATH = "instance-reboot"
336
  HTYPE = constants.HTYPE_INSTANCE
337
  REQ_BGL = False
338

    
339
  def ExpandNames(self):
340
    self._ExpandAndLockInstance()
341

    
342
  def BuildHooksEnv(self):
343
    """Build hooks env.
344

345
    This runs on master, primary and secondary nodes of the instance.
346

347
    """
348
    env = {
349
      "IGNORE_SECONDARIES": self.op.ignore_secondaries,
350
      "REBOOT_TYPE": self.op.reboot_type,
351
      "SHUTDOWN_TIMEOUT": self.op.shutdown_timeout,
352
      }
353

    
354
    env.update(BuildInstanceHookEnvByObject(self, self.instance))
355

    
356
    return env
357

    
358
  def BuildHooksNodes(self):
359
    """Build hooks nodes.
360

361
    """
362
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
363
    return (nl, nl)
364

    
365
  def CheckPrereq(self):
366
    """Check prerequisites.
367

368
    This checks that the instance is in the cluster.
369

370
    """
371
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_uuid)
372
    assert self.instance is not None, \
373
      "Cannot retrieve locked instance %s" % self.op.instance_name
374
    CheckInstanceState(self, self.instance, INSTANCE_ONLINE)
375
    CheckNodeOnline(self, self.instance.primary_node)
376

    
377
    # check bridges existence
378
    CheckInstanceBridgesExist(self, self.instance)
379

    
380
  def Exec(self, feedback_fn):
381
    """Reboot the instance.
382

383
    """
384
    cluster = self.cfg.GetClusterInfo()
385
    remote_info = self.rpc.call_instance_info(
386
        self.instance.primary_node, self.instance.name,
387
        self.instance.hypervisor, cluster.hvparams[self.instance.hypervisor])
388
    remote_info.Raise("Error checking node %s" %
389
                      self.cfg.GetNodeName(self.instance.primary_node))
390
    instance_running = bool(remote_info.payload)
391

    
392
    current_node_uuid = self.instance.primary_node
393

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

    
425
    self.cfg.MarkInstanceUp(self.instance.uuid)
426

    
427

    
428
def GetInstanceConsole(cluster, instance, primary_node):
429
  """Returns console information for an instance.
430

431
  @type cluster: L{objects.Cluster}
432
  @type instance: L{objects.Instance}
433
  @type primary_node: L{objects.Node}
434
  @rtype: dict
435

436
  """
437
  hyper = hypervisor.GetHypervisor(instance.hypervisor)
438
  # beparams and hvparams are passed separately, to avoid editing the
439
  # instance and then saving the defaults in the instance itself.
440
  hvparams = cluster.FillHV(instance)
441
  beparams = cluster.FillBE(instance)
442
  console = hyper.GetInstanceConsole(instance, primary_node, hvparams, beparams)
443

    
444
  assert console.instance == instance.name
445
  assert console.Validate()
446

    
447
  return console.ToDict()
448

    
449

    
450
class LUInstanceConsole(NoHooksLU):
451
  """Connect to an instance's console.
452

453
  This is somewhat special in that it returns the command line that
454
  you need to run on the master node in order to connect to the
455
  console.
456

457
  """
458
  REQ_BGL = False
459

    
460
  def ExpandNames(self):
461
    self.share_locks = ShareAll()
462
    self._ExpandAndLockInstance()
463

    
464
  def CheckPrereq(self):
465
    """Check prerequisites.
466

467
    This checks that the instance is in the cluster.
468

469
    """
470
    self.instance = self.cfg.GetInstanceInfo(self.op.instance_uuid)
471
    assert self.instance is not None, \
472
      "Cannot retrieve locked instance %s" % self.op.instance_name
473
    CheckNodeOnline(self, self.instance.primary_node)
474

    
475
  def Exec(self, feedback_fn):
476
    """Connect to the console of an instance
477

478
    """
479
    node_uuid = self.instance.primary_node
480

    
481
    cluster_hvparams = self.cfg.GetClusterInfo().hvparams
482
    node_insts = self.rpc.call_instance_list(
483
                   [node_uuid], [self.instance.hypervisor],
484
                   cluster_hvparams)[node_uuid]
485
    node_insts.Raise("Can't get node information from %s" %
486
                     self.cfg.GetNodeName(node_uuid))
487

    
488
    if self.instance.name not in node_insts.payload:
489
      if self.instance.admin_state == constants.ADMINST_UP:
490
        state = constants.INSTST_ERRORDOWN
491
      elif self.instance.admin_state == constants.ADMINST_DOWN:
492
        state = constants.INSTST_ADMINDOWN
493
      else:
494
        state = constants.INSTST_ADMINOFFLINE
495
      raise errors.OpExecError("Instance %s is not running (state %s)" %
496
                               (self.instance.name, state))
497

    
498
    logging.debug("Connecting to console of %s on %s", self.instance.name,
499
                  self.cfg.GetNodeName(node_uuid))
500

    
501
    return GetInstanceConsole(self.cfg.GetClusterInfo(), self.instance,
502
                              self.cfg.GetNodeInfo(self.instance.primary_node))