Revision e29e9550

b/lib/cmdlib.py
7733 7733
      self.op.beparams = {}
7734 7734
    if not hasattr(self.op, 'hvparams'):
7735 7735
      self.op.hvparams = {}
7736
    if not hasattr(self.op, "disk_template"):
7737
      self.op.disk_template = None
7738
    if not hasattr(self.op, "remote_node"):
7739
      self.op.remote_node = None
7736 7740
    self.op.force = getattr(self.op, "force", False)
7737
    if not (self.op.nics or self.op.disks or
7741
    if not (self.op.nics or self.op.disks or self.op.disk_template or
7738 7742
            self.op.hvparams or self.op.beparams):
7739 7743
      raise errors.OpPrereqError("No changes submitted", errors.ECODE_INVAL)
7740 7744

  
......
7781 7785
      raise errors.OpPrereqError("Only one disk add or remove operation"
7782 7786
                                 " supported at a time", errors.ECODE_INVAL)
7783 7787

  
7788
    if self.op.disks and self.op.disk_template is not None:
7789
      raise errors.OpPrereqError("Disk template conversion and other disk"
7790
                                 " changes not supported at the same time",
7791
                                 errors.ECODE_INVAL)
7792

  
7793
    if self.op.disk_template:
7794
      _CheckDiskTemplate(self.op.disk_template)
7795
      if (self.op.disk_template in constants.DTS_NET_MIRROR and
7796
          self.op.remote_node is None):
7797
        raise errors.OpPrereqError("Changing the disk template to a mirrored"
7798
                                   " one requires specifying a secondary node",
7799
                                   errors.ECODE_INVAL)
7800

  
7784 7801
    # NIC validation
7785 7802
    nic_addremove = 0
7786 7803
    for nic_op, nic_dict in self.op.nics:
......
7843 7860
  def DeclareLocks(self, level):
7844 7861
    if level == locking.LEVEL_NODE:
7845 7862
      self._LockInstancesNodes()
7863
      if self.op.disk_template and self.op.remote_node:
7864
        self.op.remote_node = _ExpandNodeName(self.cfg, self.op.remote_node)
7865
        self.needed_locks[locking.LEVEL_NODE].append(self.op.remote_node)
7846 7866

  
7847 7867
  def BuildHooksEnv(self):
7848 7868
    """Build hooks env.
......
7892 7912
        del args['nics'][-1]
7893 7913

  
7894 7914
    env = _BuildInstanceHookEnvByObject(self, self.instance, override=args)
7915
    if self.op.disk_template:
7916
      env["NEW_DISK_TEMPLATE"] = self.op.disk_template
7895 7917
    nl = [self.cfg.GetMasterNode()] + list(self.instance.all_nodes)
7896 7918
    return env, nl, nl
7897 7919

  
......
7945 7967
    pnode = instance.primary_node
7946 7968
    nodelist = list(instance.all_nodes)
7947 7969

  
7970
    if self.op.disk_template:
7971
      if instance.disk_template == self.op.disk_template:
7972
        raise errors.OpPrereqError("Instance already has disk template %s" %
7973
                                   instance.disk_template, errors.ECODE_INVAL)
7974

  
7975
      if (instance.disk_template,
7976
          self.op.disk_template) not in self._DISK_CONVERSIONS:
7977
        raise errors.OpPrereqError("Unsupported disk template conversion from"
7978
                                   " %s to %s" % (instance.disk_template,
7979
                                                  self.op.disk_template),
7980
                                   errors.ECODE_INVAL)
7981
      if self.op.disk_template in constants.DTS_NET_MIRROR:
7982
        _CheckNodeOnline(self, self.op.remote_node)
7983
        _CheckNodeNotDrained(self, self.op.remote_node)
7984
        disks = [{"size": d.size} for d in instance.disks]
7985
        required = _ComputeDiskSize(self.op.disk_template, disks)
7986
        _CheckNodesFreeDisk(self, [self.op.remote_node], required)
7987
        _CheckInstanceDown(self, instance, "cannot change disk template")
7988

  
7948 7989
    # hvparams processing
7949 7990
    if self.op.hvparams:
7950 7991
      i_hvdict, hv_new = self._GetUpdatedParams(
......
8128 8169

  
8129 8170
    return
8130 8171

  
8172
  def _ConvertPlainToDrbd(self, feedback_fn):
8173
    """Converts an instance from plain to drbd.
8174

  
8175
    """
8176
    feedback_fn("Converting template to drbd")
8177
    instance = self.instance
8178
    pnode = instance.primary_node
8179
    snode = self.op.remote_node
8180

  
8181
    # create a fake disk info for _GenerateDiskTemplate
8182
    disk_info = [{"size": d.size, "mode": d.mode} for d in instance.disks]
8183
    new_disks = _GenerateDiskTemplate(self, self.op.disk_template,
8184
                                      instance.name, pnode, [snode],
8185
                                      disk_info, None, None, 0)
8186
    info = _GetInstanceInfoText(instance)
8187
    feedback_fn("Creating aditional volumes...")
8188
    # first, create the missing data and meta devices
8189
    for disk in new_disks:
8190
      # unfortunately this is... not too nice
8191
      _CreateSingleBlockDev(self, pnode, instance, disk.children[1],
8192
                            info, True)
8193
      for child in disk.children:
8194
        _CreateSingleBlockDev(self, snode, instance, child, info, True)
8195
    # at this stage, all new LVs have been created, we can rename the
8196
    # old ones
8197
    feedback_fn("Renaming original volumes...")
8198
    rename_list = [(o, n.children[0].logical_id)
8199
                   for (o, n) in zip(instance.disks, new_disks)]
8200
    result = self.rpc.call_blockdev_rename(pnode, rename_list)
8201
    result.Raise("Failed to rename original LVs")
8202

  
8203
    feedback_fn("Initializing DRBD devices...")
8204
    # all child devices are in place, we can now create the DRBD devices
8205
    for disk in new_disks:
8206
      for node in [pnode, snode]:
8207
        f_create = node == pnode
8208
        _CreateSingleBlockDev(self, node, instance, disk, info, f_create)
8209

  
8210
    # at this point, the instance has been modified
8211
    instance.disk_template = constants.DT_DRBD8
8212
    instance.disks = new_disks
8213
    self.cfg.Update(instance, feedback_fn)
8214

  
8215
    # disks are created, waiting for sync
8216
    disk_abort = not _WaitForSync(self, instance)
8217
    if disk_abort:
8218
      raise errors.OpExecError("There are some degraded disks for"
8219
                               " this instance, please cleanup manually")
8220

  
8131 8221
  def Exec(self, feedback_fn):
8132 8222
    """Modifies an instance.
8133 8223

  
......
8192 8282
        # change a given disk
8193 8283
        instance.disks[disk_op].mode = disk_dict['mode']
8194 8284
        result.append(("disk.mode/%d" % disk_op, disk_dict['mode']))
8285

  
8286
    if self.op.disk_template:
8287
      r_shut = _ShutdownInstanceDisks(self, instance)
8288
      if not r_shut:
8289
        raise errors.OpExecError("Cannot shutdow instance disks, unable to"
8290
                                 " proceed with disk template conversion")
8291
      mode = (instance.disk_template, self.op.disk_template)
8292
      try:
8293
        self._DISK_CONVERSIONS[mode](self, feedback_fn)
8294
      except:
8295
        self.cfg.ReleaseDRBDMinors(instance.name)
8296
        raise
8297
      result.append(("disk_template", self.op.disk_template))
8298

  
8195 8299
    # NIC changes
8196 8300
    for nic_op, nic_dict in self.op.nics:
8197 8301
      if nic_op == constants.DDM_REMOVE:
......
8236 8340

  
8237 8341
    return result
8238 8342

  
8343
  _DISK_CONVERSIONS = {
8344
    (constants.DT_PLAIN, constants.DT_DRBD8): _ConvertPlainToDrbd,
8345
    }
8239 8346

  
8240 8347
class LUQueryExports(NoHooksLU):
8241 8348
  """Query the exports list
b/lib/opcodes.py
620 620
  __slots__ = [
621 621
    "instance_name",
622 622
    "hvparams", "beparams", "force",
623
    "nics", "disks",
623
    "nics", "disks", "disk_template",
624
    "remote_node",
624 625
    ]
625 626

  
626 627

  
b/scripts/gnt-instance
1207 1207
  @return: the desired exit code
1208 1208

  
1209 1209
  """
1210
  if not (opts.nics or opts.disks or
1210
  if not (opts.nics or opts.disks or opts.disk_template or
1211 1211
          opts.hvparams or opts.beparams):
1212 1212
    ToStderr("Please give at least one of the parameters.")
1213 1213
    return 1
......
1247 1247
                                   errors.ECODE_INVAL)
1248 1248
      disk_dict['size'] = utils.ParseUnit(disk_dict['size'])
1249 1249

  
1250
  if (opts.disk_template and
1251
      opts.disk_template in constants.DTS_NET_MIRROR and
1252
      not opts.node):
1253
    ToStderr("Changing the disk template to a mirrored one requires"
1254
             " specifying a secondary node")
1255
    return 1
1256

  
1250 1257
  op = opcodes.OpSetInstanceParams(instance_name=args[0],
1251 1258
                                   nics=opts.nics,
1252 1259
                                   disks=opts.disks,
1260
                                   disk_template=opts.disk_template,
1261
                                   remote_node=opts.node,
1253 1262
                                   hvparams=opts.hvparams,
1254 1263
                                   beparams=opts.beparams,
1255 1264
                                   force=opts.force)
......
1261 1270
    ToStdout("Modified instance %s", args[0])
1262 1271
    for param, data in result:
1263 1272
      ToStdout(" - %-5s -> %s", param, data)
1264
    ToStdout("Please don't forget that these parameters take effect"
1273
    ToStdout("Please don't forget that most parameters take effect"
1265 1274
             " only at the next start of the instance.")
1266 1275
  return 0
1267 1276

  
......
1407 1416
    "Replaces all disks for the instance"),
1408 1417
  'modify': (
1409 1418
    SetInstanceParams, ARGS_ONE_INSTANCE,
1410
    [BACKEND_OPT, DISK_OPT, FORCE_OPT, HVOPTS_OPT, NET_OPT, SUBMIT_OPT],
1419
    [BACKEND_OPT, DISK_OPT, FORCE_OPT, HVOPTS_OPT, NET_OPT, SUBMIT_OPT,
1420
     DISK_TEMPLATE_OPT, SINGLE_NODE_OPT],
1411 1421
    "<instance>", "Alters the parameters of an instance"),
1412 1422
  'shutdown': (
1413 1423
    GenericManyOps("shutdown", _ShutdownInstance), [ArgInstance()],

Also available in: Unified diff