commit 08db7c5cd2748c55676018f2aad9499b0f5530dc
Author: Iustin Pop <iustin@google.com>
Date:   Thu Nov 20 06:22:39 2008 +0000

    Initial multi-disk/multi-nic support
    
    This patch adds support for mult-disk/multi-nic in:
      - instance add
      - burnin
    
    The start/stop/failover/cluster verify work as expected. Replace disk
    and grow disk are TODO.
    
    There's also a change gnt-job to allow dictionaries to be listed in
    gnt-job info.
    
    Reviewed-by: imsnah

diff --git a/lib/cmdlib.py b/lib/cmdlib.py
index f5e2b96..4b66c19 100644
--- a/lib/cmdlib.py
+++ b/lib/cmdlib.py
@@ -3141,7 +3141,7 @@ def _GenerateDRBD8Branch(lu, primary, secondary, size, names, iv_name,
 
 def _GenerateDiskTemplate(lu, template_name,
                           instance_name, primary_node,
-                          secondary_nodes, disk_sz, swap_sz,
+                          secondary_nodes, disk_info,
                           file_storage_dir, file_driver):
   """Generate the entire disk layout for a given template type.
 
@@ -3149,48 +3149,51 @@ def _GenerateDiskTemplate(lu, template_name,
   #TODO: compute space requirements
 
   vgname = lu.cfg.GetVGName()
+  disk_count = len(disk_info)
+  disks = []
   if template_name == constants.DT_DISKLESS:
-    disks = []
+    pass
   elif template_name == constants.DT_PLAIN:
     if len(secondary_nodes) != 0:
       raise errors.ProgrammerError("Wrong template configuration")
 
-    names = _GenerateUniqueNames(lu, [".sda", ".sdb"])
-    sda_dev = objects.Disk(dev_type=constants.LD_LV, size=disk_sz,
-                           logical_id=(vgname, names[0]),
-                           iv_name = "sda")
-    sdb_dev = objects.Disk(dev_type=constants.LD_LV, size=swap_sz,
-                           logical_id=(vgname, names[1]),
-                           iv_name = "sdb")
-    disks = [sda_dev, sdb_dev]
+    names = _GenerateUniqueNames(lu, [".disk%d" % i
+                                      for i in range(disk_count)])
+    for idx, disk in enumerate(disk_info):
+      disk_dev = objects.Disk(dev_type=constants.LD_LV, size=disk["size"],
+                              logical_id=(vgname, names[idx]),
+                              iv_name = "disk/%d" % idx)
+      disks.append(disk_dev)
   elif template_name == constants.DT_DRBD8:
     if len(secondary_nodes) != 1:
       raise errors.ProgrammerError("Wrong template configuration")
     remote_node = secondary_nodes[0]
-    (minor_pa, minor_pb,
-     minor_sa, minor_sb) = lu.cfg.AllocateDRBDMinor(
-      [primary_node, primary_node, remote_node, remote_node], instance_name)
-
-    names = _GenerateUniqueNames(lu, [".sda_data", ".sda_meta",
-                                      ".sdb_data", ".sdb_meta"])
-    drbd_sda_dev = _GenerateDRBD8Branch(lu, primary_node, remote_node,
-                                        disk_sz, names[0:2], "sda",
-                                        minor_pa, minor_sa)
-    drbd_sdb_dev = _GenerateDRBD8Branch(lu, primary_node, remote_node,
-                                        swap_sz, names[2:4], "sdb",
-                                        minor_pb, minor_sb)
-    disks = [drbd_sda_dev, drbd_sdb_dev]
+    minors = lu.cfg.AllocateDRBDMinor(
+      [primary_node, remote_node] * len(disk_info), instance_name)
+
+    names = _GenerateUniqueNames(lu,
+                                 [".disk%d_%s" % (i, s)
+                                  for i in range(disk_count)
+                                  for s in ("data", "meta")
+                                  ])
+    for idx, disk in enumerate(disk_info):
+      disk_dev = _GenerateDRBD8Branch(lu, primary_node, remote_node,
+                                      disk["size"], names[idx*2:idx*2+2],
+                                      "disk/%d" % idx,
+                                      minors[idx*2], minors[idx*2+1])
+      disks.append(disk_dev)
   elif template_name == constants.DT_FILE:
     if len(secondary_nodes) != 0:
       raise errors.ProgrammerError("Wrong template configuration")
 
-    file_sda_dev = objects.Disk(dev_type=constants.LD_FILE, size=disk_sz,
-                                iv_name="sda", logical_id=(file_driver,
-                                "%s/sda" % file_storage_dir))
-    file_sdb_dev = objects.Disk(dev_type=constants.LD_FILE, size=swap_sz,
-                                iv_name="sdb", logical_id=(file_driver,
-                                "%s/sdb" % file_storage_dir))
-    disks = [file_sda_dev, file_sdb_dev]
+    for idx, disk in enumerate(disk_info):
+
+      disk_dev = objects.Disk(dev_type=constants.LD_FILE, size=disk["size"],
+                              iv_name="disk/%d" % idx,
+                              logical_id=(file_driver,
+                                          "%s/disk%d" % (file_storage_dir,
+                                                         idx)))
+      disks.append(disk_dev)
   else:
     raise errors.ProgrammerError("Invalid disk template '%s'" % template_name)
   return disks
@@ -3285,7 +3288,7 @@ def _RemoveDisks(lu, instance):
   return result
 
 
-def _ComputeDiskSize(disk_template, disk_size, swap_size):
+def _ComputeDiskSize(disk_template, disks):
   """Compute disk size requirements in the volume group
 
   This is currently hard-coded for the two-drive layout.
@@ -3294,9 +3297,9 @@ def _ComputeDiskSize(disk_template, disk_size, swap_size):
   # Required free disk space as a function of disk and swap space
   req_size_dict = {
     constants.DT_DISKLESS: None,
-    constants.DT_PLAIN: disk_size + swap_size,
-    # 256 MB are added for drbd metadata, 128MB for each drbd device
-    constants.DT_DRBD8: disk_size + swap_size + 256,
+    constants.DT_PLAIN: sum(d["size"] for d in disks),
+    # 128 MB are added for drbd metadata for each disk
+    constants.DT_DRBD8: sum(d["size"] + 128 for d in disks),
     constants.DT_FILE: None,
   }
 
@@ -3343,9 +3346,9 @@ class LUCreateInstance(LogicalUnit):
   """
   HPATH = "instance-add"
   HTYPE = constants.HTYPE_INSTANCE
-  _OP_REQP = ["instance_name", "disk_size",
-              "disk_template", "swap_size", "mode", "start",
-              "wait_for_sync", "ip_check", "mac",
+  _OP_REQP = ["instance_name", "disks", "disk_template",
+              "mode", "start",
+              "wait_for_sync", "ip_check", "nics",
               "hvparams", "beparams"]
   REQ_BGL = False
 
@@ -3418,27 +3421,50 @@ class LUCreateInstance(LogicalUnit):
 
     self.add_locks[locking.LEVEL_INSTANCE] = instance_name
 
-    # ip validity checks
-    ip = getattr(self.op, "ip", None)
-    if ip is None or ip.lower() == "none":
-      inst_ip = None
-    elif ip.lower() == constants.VALUE_AUTO:
-      inst_ip = hostname1.ip
-    else:
-      if not utils.IsValidIP(ip):
-        raise errors.OpPrereqError("given IP address '%s' doesn't look"
-                                   " like a valid IP" % ip)
-      inst_ip = ip
-    self.inst_ip = self.op.ip = inst_ip
+    # NIC buildup
+    self.nics = []
+    for nic in self.op.nics:
+      # ip validity checks
+      ip = nic.get("ip", None)
+      if ip is None or ip.lower() == "none":
+        nic_ip = None
+      elif ip.lower() == constants.VALUE_AUTO:
+        nic_ip = hostname1.ip
+      else:
+        if not utils.IsValidIP(ip):
+          raise errors.OpPrereqError("Given IP address '%s' doesn't look"
+                                     " like a valid IP" % ip)
+        nic_ip = ip
+
+      # MAC address verification
+      mac = nic.get("mac", constants.VALUE_AUTO)
+      if mac not in (constants.VALUE_AUTO, constants.VALUE_GENERATE):
+        if not utils.IsValidMac(mac.lower()):
+          raise errors.OpPrereqError("Invalid MAC address specified: %s" %
+                                     mac)
+      # bridge verification
+      bridge = nic.get("bridge", self.cfg.GetDefBridge())
+      self.nics.append(objects.NIC(mac=mac, ip=nic_ip, bridge=bridge))
+
+    # disk checks/pre-build
+    self.disks = []
+    for disk in self.op.disks:
+      mode = disk.get("mode", constants.DISK_RDWR)
+      if mode not in constants.DISK_ACCESS_SET:
+        raise errors.OpPrereqError("Invalid disk access mode '%s'" %
+                                   mode)
+      size = disk.get("size", None)
+      if size is None:
+        raise errors.OpPrereqError("Missing disk size")
+      try:
+        size = int(size)
+      except ValueError:
+        raise errors.OpPrereqError("Invalid disk size '%s'" % size)
+      self.disks.append({"size": size, "mode": mode})
+
     # used in CheckPrereq for ip ping check
     self.check_ip = hostname1.ip
 
-    # MAC address verification
-    if self.op.mac not in (constants.VALUE_AUTO, constants.VALUE_GENERATE):
-      if not utils.IsValidMac(self.op.mac.lower()):
-        raise errors.OpPrereqError("invalid MAC address specified: %s" %
-                                   self.op.mac)
-
     # file storage checks
     if (self.op.file_driver and
         not self.op.file_driver in constants.FILE_DRIVER):
@@ -3487,10 +3513,7 @@ class LUCreateInstance(LogicalUnit):
     """Run the allocator based on input opcode.
 
     """
-    disks = [{"size": self.op.disk_size, "mode": "w"},
-             {"size": self.op.swap_size, "mode": "w"}]
-    nics = [{"mac": self.op.mac, "ip": getattr(self.op, "ip", None),
-             "bridge": self.op.bridge}]
+    nics = [n.ToDict() for n in self.nics]
     ial = IAllocator(self,
                      mode=constants.IALLOCATOR_MODE_ALLOC,
                      name=self.op.instance_name,
@@ -3499,7 +3522,7 @@ class LUCreateInstance(LogicalUnit):
                      os=self.op.os_type,
                      vcpus=self.be_full[constants.BE_VCPUS],
                      mem_size=self.be_full[constants.BE_MEMORY],
-                     disks=disks,
+                     disks=self.disks,
                      nics=nics,
                      )
 
@@ -3529,8 +3552,7 @@ class LUCreateInstance(LogicalUnit):
     """
     env = {
       "INSTANCE_DISK_TEMPLATE": self.op.disk_template,
-      "INSTANCE_DISK_SIZE": self.op.disk_size,
-      "INSTANCE_SWAP_SIZE": self.op.swap_size,
+      "INSTANCE_DISK_SIZE": ",".join(str(d["size"]) for d in self.disks),
       "INSTANCE_ADD_MODE": self.op.mode,
       }
     if self.op.mode == constants.INSTANCE_IMPORT:
@@ -3545,7 +3567,7 @@ class LUCreateInstance(LogicalUnit):
       os_type=self.op.os_type,
       memory=self.be_full[constants.BE_MEMORY],
       vcpus=self.be_full[constants.BE_VCPUS],
-      nics=[(self.inst_ip, self.op.bridge, self.op.mac)],
+      nics=[(n.ip, n.bridge, n.mac) for n in self.nics],
     ))
 
     nl = ([self.cfg.GetMasterNode(), self.op.pnode] +
@@ -3581,8 +3603,7 @@ class LUCreateInstance(LogicalUnit):
                                    (ei_version, constants.EXPORT_VERSION))
 
       # Check that the new instance doesn't have less disks than the export
-      # TODO: substitute "2" with the actual number of disks requested
-      instance_disks = 2
+      instance_disks = len(self.disks)
       export_disks = export_info.getint(constants.INISECT_INS, 'disk_count')
       if instance_disks < export_disks:
         raise errors.OpPrereqError("Not enough disks to import."
@@ -3622,13 +3643,6 @@ class LUCreateInstance(LogicalUnit):
         raise errors.OpPrereqError("IP %s of instance %s already in use" %
                                    (self.check_ip, self.op.instance_name))
 
-    # bridge verification
-    bridge = getattr(self.op, "bridge", None)
-    if bridge is None:
-      self.op.bridge = self.cfg.GetDefBridge()
-    else:
-      self.op.bridge = bridge
-
     #### allocator run
 
     if self.op.iallocator is not None:
@@ -3655,7 +3669,7 @@ class LUCreateInstance(LogicalUnit):
     nodenames = [pnode.name] + self.secondaries
 
     req_size = _ComputeDiskSize(self.op.disk_template,
-                                self.op.disk_size, self.op.swap_size)
+                                self.disks)
 
     # Check lv size requirements
     if req_size is not None:
@@ -3684,10 +3698,12 @@ class LUCreateInstance(LogicalUnit):
                                  " primary node"  % self.op.os_type)
 
     # bridge check on primary node
-    if not self.rpc.call_bridges_exist(self.pnode.name, [self.op.bridge]):
-      raise errors.OpPrereqError("target bridge '%s' does not exist on"
+    bridges = [n.bridge for n in self.nics]
+    if not self.rpc.call_bridges_exist(self.pnode.name, bridges):
+      raise errors.OpPrereqError("one of the target bridges '%s' does not"
+                                 " exist on"
                                  " destination node '%s'" %
-                                 (self.op.bridge, pnode.name))
+                                 (",".join(bridges), pnode.name))
 
     # memory check on primary node
     if self.op.start:
@@ -3708,14 +3724,9 @@ class LUCreateInstance(LogicalUnit):
     instance = self.op.instance_name
     pnode_name = self.pnode.name
 
-    if self.op.mac in (constants.VALUE_AUTO, constants.VALUE_GENERATE):
-      mac_address = self.cfg.GenerateMAC()
-    else:
-      mac_address = self.op.mac
-
-    nic = objects.NIC(bridge=self.op.bridge, mac=mac_address)
-    if self.inst_ip is not None:
-      nic.ip = self.inst_ip
+    for nic in self.nics:
+      if nic.mac in (constants.VALUE_AUTO, constants.VALUE_GENERATE):
+        nic.mac = self.cfg.GenerateMAC()
 
     ht_kind = self.op.hypervisor
     if ht_kind in constants.HTS_REQ_PORT:
@@ -3741,14 +3752,14 @@ class LUCreateInstance(LogicalUnit):
     disks = _GenerateDiskTemplate(self,
                                   self.op.disk_template,
                                   instance, pnode_name,
-                                  self.secondaries, self.op.disk_size,
-                                  self.op.swap_size,
+                                  self.secondaries,
+                                  self.disks,
                                   file_storage_dir,
                                   self.op.file_driver)
 
     iobj = objects.Instance(name=instance, os=self.op.os_type,
                             primary_node=pnode_name,
-                            nics=[nic], disks=disks,
+                            nics=self.nics, disks=disks,
                             disk_template=self.op.disk_template,
                             status=self.instance_status,
                             network_port=network_port,
diff --git a/lib/constants.py b/lib/constants.py
index 925432a..1972303 100644
--- a/lib/constants.py
+++ b/lib/constants.py
@@ -190,6 +190,11 @@ FD_BLKTAP = "blktap"
 # the set of drbd-like disk types
 LDS_DRBD = frozenset([LD_DRBD8])
 
+# disk access mode
+DISK_RDONLY = "r"
+DISK_RDWR = "w"
+DISK_ACCESS_SET = frozenset([DISK_RDONLY, DISK_RDWR])
+
 # disk replacement mode
 REPLACE_DISK_PRI = "replace_primary"
 REPLACE_DISK_SEC = "replace_secondary"
diff --git a/lib/objects.py b/lib/objects.py
index 6c74876..26cdc3d 100644
--- a/lib/objects.py
+++ b/lib/objects.py
@@ -274,7 +274,7 @@ class NIC(ConfigObject):
 class Disk(ConfigObject):
   """Config object representing a block device."""
   __slots__ = ["dev_type", "logical_id", "physical_id",
-               "children", "iv_name", "size"]
+               "children", "iv_name", "size", "mode"]
 
   def CreateOnSecondary(self):
     """Test if this device needs to be created on a secondary node."""
diff --git a/lib/opcodes.py b/lib/opcodes.py
index 533215f..9b178c5 100644
--- a/lib/opcodes.py
+++ b/lib/opcodes.py
@@ -319,10 +319,11 @@ class OpCreateInstance(OpCode):
   OP_ID = "OP_INSTANCE_CREATE"
   OP_DSC_FIELD = "instance_name"
   __slots__ = [
-    "instance_name", "disk_size", "os_type", "pnode",
-    "disk_template", "snode", "swap_size", "mode",
-    "ip", "bridge", "src_node", "src_path", "start",
-    "wait_for_sync", "ip_check", "mac",
+    "instance_name", "os_type", "pnode",
+    "disk_template", "snode", "mode",
+    "disks", "nics",
+    "src_node", "src_path", "start",
+    "wait_for_sync", "ip_check",
     "file_storage_dir", "file_driver",
     "iallocator",
     "hypervisor", "hvparams", "beparams",
diff --git a/scripts/gnt-instance b/scripts/gnt-instance
index 4802328..5b4162c 100755
--- a/scripts/gnt-instance
+++ b/scripts/gnt-instance
@@ -293,6 +293,40 @@ def AddInstance(opts, args):
   if opts.hypervisor:
     hypervisor, hvparams = opts.hypervisor
 
+  if opts.nics:
+    try:
+      nic_max = max(int(nidx[0])+1 for nidx in opts.nics)
+    except ValueError, err:
+      raise errors.OpPrereqError("Invalid NIC index passed: %s" % str(err))
+    nics = [{}] * nic_max
+    for nidx, ndict in opts.nics.items():
+      nidx = int(nidx)
+      nics[nidx] = ndict
+  else:
+    # default of one nic, all auto
+    nics = [{}]
+
+  if not opts.disks and opts.disk_template != constants.DT_DISKLESS:
+    raise errors.OpPrereqError("No disk information specified")
+  elif opts.disks and opts.disk_template == constants.DT_DISKLESS:
+    raise errors.OpPrereqError("Diskless instance but disk information passeD")
+  else:
+    try:
+      disk_max = max(int(didx[0])+1 for didx in opts.disks)
+    except ValueError, err:
+      raise errors.OpPrereqError("Invalid disk index passed: %s" % str(err))
+    disks = [{}] * disk_max
+    for didx, ddict in opts.disks:
+      didx = int(didx)
+      if "size" not in ddict:
+        raise errors.OpPrereqError("Missing size for disk %d" % didx)
+      try:
+        ddict["size"] = utils.ParseUnit(ddict["size"])
+      except ValueError, err:
+        raise errors.OpPrereqError("Invalid disk size for disk %d: %s" %
+                                   (didx, err))
+      disks[didx] = ddict
+
   ValidateBeParams(opts.beparams)
 
 ##  kernel_path = _TransformPath(opts.kernel_path)
@@ -308,15 +342,14 @@ def AddInstance(opts, args):
 ##    hvm_cdrom_image_path = opts.hvm_cdrom_image_path
 
   op = opcodes.OpCreateInstance(instance_name=instance,
-                                disk_size=opts.size, swap_size=opts.swap,
+                                disks=disks,
                                 disk_template=opts.disk_template,
+                                nics=nics,
                                 mode=constants.INSTANCE_CREATE,
                                 os_type=opts.os, pnode=pnode,
                                 snode=snode,
-                                ip=opts.ip, bridge=opts.bridge,
                                 start=opts.start, ip_check=opts.ip_check,
                                 wait_for_sync=opts.wait_for_sync,
-                                mac=opts.mac,
                                 hypervisor=hypervisor,
                                 hvparams=hvparams,
                                 beparams=opts.beparams,
@@ -1075,17 +1108,16 @@ add_opts = [
   make_option("-t", "--disk-template", dest="disk_template",
               help="Custom disk setup (diskless, file, plain or drbd)",
               default=None, metavar="TEMPL"),
-  make_option("-i", "--ip", dest="ip",
-              help="IP address ('none' [default], 'auto', or specify address)",
-              default='none', type="string", metavar="<ADDRESS>"),
-  make_option("--mac", dest="mac",
-              help="MAC address ('auto' [default], or specify address)",
-              default='auto', type="string", metavar="<MACADDRESS>"),
+  ikv_option("--disk", help="Disk information",
+             default=[], dest="disks",
+             action="append",
+             type="identkeyval"),
+  ikv_option("--net", help="NIC information",
+             default=[], dest="nics",
+             action="append",
+             type="identkeyval"),
   make_option("--no-wait-for-sync", dest="wait_for_sync", default=True,
               action="store_false", help="Don't wait for sync (DANGEROUS!)"),
-  make_option("-b", "--bridge", dest="bridge",
-              help="Bridge to connect this instance to",
-              default=None, metavar="<bridge>"),
   make_option("--no-start", dest="start", default=True,
               action="store_false", help="Don't start the instance after"
               " creation"),
diff --git a/scripts/gnt-job b/scripts/gnt-job
index 29df6a9..39f0743 100755
--- a/scripts/gnt-job
+++ b/scripts/gnt-job
@@ -280,7 +280,7 @@ def ShowJobs(opts, args):
         if key == "OP_ID":
           continue
         if isinstance(val, (tuple, list)):
-          val = ",".join(val)
+          val = ",".join([str(item) for item in val])
         format(4, "%s: %s" % (key, val))
       if result is None:
         format(3, "No output data")
diff --git a/tools/burnin b/tools/burnin
index 295ea7d..d328d47 100755
--- a/tools/burnin
+++ b/tools/burnin
@@ -126,14 +126,11 @@ class Burner(object):
     parser.add_option("-o", "--os", dest="os", default=None,
                       help="OS to use during burnin",
                       metavar="<OS>")
-    parser.add_option("--os-size", dest="os_size", help="Disk size",
-                      default=4 * 1024, type="unit", metavar="<size>")
-    parser.add_option("--os-growth", dest="sda_growth", help="Disk growth",
-                      default=1024, type="unit", metavar="<size>")
-    parser.add_option("--swap-size", dest="swap_size", help="Swap size",
-                      default=4 * 1024, type="unit", metavar="<size>")
-    parser.add_option("--swap-growth", dest="sdb_growth", help="Swap growth",
-                      default=1024, type="unit", metavar="<size>")
+    parser.add_option("--disk-size", dest="disk_size",
+                      help="Disk size (determines disk count)",
+                      default="128m", type="string", metavar="<size,size,...>")
+    parser.add_option("--disk-growth", dest="disk_growth", help="Disk growth",
+                      default=128, type="string", metavar="<size,size,...>")
     parser.add_option("--mem-size", dest="mem_size", help="Memory size",
                       default=128, type="unit", metavar="<size>")
     parser.add_option("-v", "--verbose",
@@ -188,6 +185,20 @@ class Burner(object):
       Log("Unknown disk template '%s'" % options.disk_template)
       sys.exit(1)
 
+    disk_size = [utils.ParseUnit(v) for v in options.disk_size.split(",")]
+    disk_growth = [utils.ParseUnit(v) for v in options.disk_growth.split(",")]
+    if len(disk_growth) != len(disk_size):
+      Log("Wrong disk sizes/growth combination")
+      sys.exit(1)
+    if ((disk_size and options.disk_template == constants.DT_DISKLESS) or
+        (not disk_size and options.disk_template != constants.DT_DISKLESS)):
+      Log("Wrong disk count/disk template combination")
+      sys.exit(1)
+
+    self.disk_size = disk_size
+    self.disk_growth = disk_growth
+    self.disk_count = len(disk_size)
+
     if options.nodes and options.iallocator:
       Log("Give either the nodes option or the iallocator option, not both")
       sys.exit(1)
@@ -251,9 +262,10 @@ class Burner(object):
         Log("- Add instance %s on nodes %s/%s" % (instance, pnode, snode))
 
       op = opcodes.OpCreateInstance(instance_name=instance,
-                                    disk_size=self.opts.os_size,
-                                    swap_size=self.opts.swap_size,
+                                    disks = [ {"size": size}
+                                              for size in self.disk_size],
                                     disk_template=self.opts.disk_template,
+                                    nics=[{}],
                                     mode=constants.INSTANCE_CREATE,
                                     os_type=self.opts.os,
                                     pnode=pnode,
@@ -261,7 +273,6 @@ class Burner(object):
                                     start=True,
                                     ip_check=True,
                                     wait_for_sync=True,
-                                    mac="auto",
                                     file_driver="loop",
                                     file_storage_dir=None,
                                     iallocator=self.opts.iallocator,
@@ -283,12 +294,11 @@ class Burner(object):
   def GrowDisks(self):
     """Grow both the os and the swap disks by the requested amount, if any."""
     for instance in self.instances:
-      for disk in ['sda', 'sdb']:
-        growth = getattr(self.opts, '%s_growth' % disk)
+      for idx, growth in enumerate(self.disk_growth):
         if growth > 0:
-          op = opcodes.OpGrowDisk(instance_name=instance, disk=disk,
+          op = opcodes.OpGrowDisk(instance_name=instance, disk=idx,
                                   amount=growth, wait_for_sync=True)
-          Log("- Increase %s's %s disk by %s MB" % (instance, disk, growth))
+          Log("- Increase %s's %s disk by %s MB" % (instance, idx, growth))
           self.ExecOp(op)
 
   def ReplaceDisks1D8(self):
@@ -297,7 +307,7 @@ class Burner(object):
       for mode in constants.REPLACE_DISK_SEC, constants.REPLACE_DISK_PRI:
         op = opcodes.OpReplaceDisks(instance_name=instance,
                                     mode=mode,
-                                    disks=["sda", "sdb"])
+                                    disks=[i for i in range(self.disk_count)])
         Log("- Replace disks (%s) for instance %s" % (mode, instance))
         self.ExecOp(op)
 
@@ -314,7 +324,7 @@ class Burner(object):
                                   mode=mode,
                                   remote_node=tnode,
                                   iallocator=self.opts.iallocator,
-                                  disks=["sda", "sdb"])
+                                  disks=[i for i in range(self.disk_count)])
       Log("- Replace secondary (%s) for instance %s" % (mode, instance))
       self.ExecOp(op)
 
