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.
#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
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.
# 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,
}
"""
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
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):
"""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,
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,
)
"""
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:
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] +
(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."
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:
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:
" 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:
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:
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,
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)
## 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,
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"),
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",
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)
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,
start=True,
ip_check=True,
wait_for_sync=True,
- mac="auto",
file_driver="loop",
file_storage_dir=None,
iallocator=self.opts.iallocator,
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):
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)
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)