3228 |
3228 |
logging.error("Failed to create directory '%s'", file_storage_dir)
|
3229 |
3229 |
return False
|
3230 |
3230 |
|
|
3231 |
# Note: this needs to be kept in sync with adding of disks in
|
|
3232 |
# LUSetInstanceParams
|
3231 |
3233 |
for device in instance.disks:
|
3232 |
3234 |
logging.info("Creating volume %s for instance %s",
|
3233 |
3235 |
device.iv_name, instance.name)
|
... | ... | |
4667 |
4669 |
"""
|
4668 |
4670 |
HPATH = "instance-modify"
|
4669 |
4671 |
HTYPE = constants.HTYPE_INSTANCE
|
4670 |
|
_OP_REQP = ["instance_name", "hvparams"]
|
|
4672 |
_OP_REQP = ["instance_name"]
|
4671 |
4673 |
REQ_BGL = False
|
4672 |
4674 |
|
|
4675 |
def CheckArguments(self):
|
|
4676 |
if not hasattr(self.op, 'nics'):
|
|
4677 |
self.op.nics = []
|
|
4678 |
if not hasattr(self.op, 'disks'):
|
|
4679 |
self.op.disks = []
|
|
4680 |
if not hasattr(self.op, 'beparams'):
|
|
4681 |
self.op.beparams = {}
|
|
4682 |
if not hasattr(self.op, 'hvparams'):
|
|
4683 |
self.op.hvparams = {}
|
|
4684 |
self.op.force = getattr(self.op, "force", False)
|
|
4685 |
if not (self.op.nics or self.op.disks or
|
|
4686 |
self.op.hvparams or self.op.beparams):
|
|
4687 |
raise errors.OpPrereqError("No changes submitted")
|
|
4688 |
|
|
4689 |
for item in (constants.BE_MEMORY, constants.BE_VCPUS):
|
|
4690 |
val = self.op.beparams.get(item, None)
|
|
4691 |
if val is not None:
|
|
4692 |
try:
|
|
4693 |
val = int(val)
|
|
4694 |
except ValueError, err:
|
|
4695 |
raise errors.OpPrereqError("Invalid %s size: %s" % (item, str(err)))
|
|
4696 |
self.op.beparams[item] = val
|
|
4697 |
# Disk validation
|
|
4698 |
disk_addremove = 0
|
|
4699 |
for disk_op, disk_dict in self.op.disks:
|
|
4700 |
if disk_op == constants.DDM_REMOVE:
|
|
4701 |
disk_addremove += 1
|
|
4702 |
continue
|
|
4703 |
elif disk_op == constants.DDM_ADD:
|
|
4704 |
disk_addremove += 1
|
|
4705 |
else:
|
|
4706 |
if not isinstance(disk_op, int):
|
|
4707 |
raise errors.OpPrereqError("Invalid disk index")
|
|
4708 |
if disk_op == constants.DDM_ADD:
|
|
4709 |
mode = disk_dict.setdefault('mode', constants.DISK_RDWR)
|
|
4710 |
if mode not in (constants.DISK_RDONLY, constants.DISK_RDWR):
|
|
4711 |
raise errors.OpPrereqError("Invalid disk access mode '%s'" % mode)
|
|
4712 |
size = disk_dict.get('size', None)
|
|
4713 |
if size is None:
|
|
4714 |
raise errors.OpPrereqError("Required disk parameter size missing")
|
|
4715 |
try:
|
|
4716 |
size = int(size)
|
|
4717 |
except ValueError, err:
|
|
4718 |
raise errors.OpPrereqError("Invalid disk size parameter: %s" %
|
|
4719 |
str(err))
|
|
4720 |
disk_dict['size'] = size
|
|
4721 |
else:
|
|
4722 |
# modification of disk
|
|
4723 |
if 'size' in disk_dict:
|
|
4724 |
raise errors.OpPrereqError("Disk size change not possible, use"
|
|
4725 |
" grow-disk")
|
|
4726 |
|
|
4727 |
if disk_addremove > 1:
|
|
4728 |
raise errors.OpPrereqError("Only one disk add or remove operation"
|
|
4729 |
" supported at a time")
|
|
4730 |
|
|
4731 |
# NIC validation
|
|
4732 |
nic_addremove = 0
|
|
4733 |
for nic_op, nic_dict in self.op.nics:
|
|
4734 |
if nic_op == constants.DDM_REMOVE:
|
|
4735 |
nic_addremove += 1
|
|
4736 |
continue
|
|
4737 |
elif nic_op == constants.DDM_ADD:
|
|
4738 |
nic_addremove += 1
|
|
4739 |
else:
|
|
4740 |
if not isinstance(nic_op, int):
|
|
4741 |
raise errors.OpPrereqError("Invalid nic index")
|
|
4742 |
|
|
4743 |
# nic_dict should be a dict
|
|
4744 |
nic_ip = nic_dict.get('ip', None)
|
|
4745 |
if nic_ip is not None:
|
|
4746 |
if nic_ip.lower() == "none":
|
|
4747 |
nic_dict['ip'] = None
|
|
4748 |
else:
|
|
4749 |
if not utils.IsValidIP(nic_ip):
|
|
4750 |
raise errors.OpPrereqError("Invalid IP address '%s'" % nic_ip)
|
|
4751 |
# we can only check None bridges and assign the default one
|
|
4752 |
nic_bridge = nic_dict.get('bridge', None)
|
|
4753 |
if nic_bridge is None:
|
|
4754 |
nic_dict['bridge'] = self.cfg.GetDefBridge()
|
|
4755 |
# but we can validate MACs
|
|
4756 |
nic_mac = nic_dict.get('mac', None)
|
|
4757 |
if nic_mac is not None:
|
|
4758 |
if self.cfg.IsMacInUse(nic_mac):
|
|
4759 |
raise errors.OpPrereqError("MAC address %s already in use"
|
|
4760 |
" in cluster" % nic_mac)
|
|
4761 |
if nic_mac not in (constants.VALUE_AUTO, constants.VALUE_GENERATE):
|
|
4762 |
if not utils.IsValidMac(nic_mac):
|
|
4763 |
raise errors.OpPrereqError("Invalid MAC address %s" % nic_mac)
|
|
4764 |
if nic_addremove > 1:
|
|
4765 |
raise errors.OpPrereqError("Only one NIC add or remove operation"
|
|
4766 |
" supported at a time")
|
|
4767 |
|
4673 |
4768 |
def ExpandNames(self):
|
4674 |
4769 |
self._ExpandAndLockInstance()
|
4675 |
4770 |
self.needed_locks[locking.LEVEL_NODE] = []
|
4676 |
4771 |
self.recalculate_locks[locking.LEVEL_NODE] = constants.LOCKS_REPLACE
|
4677 |
4772 |
|
4678 |
|
|
4679 |
4773 |
def DeclareLocks(self, level):
|
4680 |
4774 |
if level == locking.LEVEL_NODE:
|
4681 |
4775 |
self._LockInstancesNodes()
|
... | ... | |
4691 |
4785 |
args['memory'] = self.be_new[constants.BE_MEMORY]
|
4692 |
4786 |
if constants.BE_VCPUS in self.be_new:
|
4693 |
4787 |
args['vcpus'] = self.be_new[constants.BE_VCPUS]
|
4694 |
|
if self.do_ip or self.do_bridge or self.mac:
|
4695 |
|
if self.do_ip:
|
4696 |
|
ip = self.ip
|
4697 |
|
else:
|
4698 |
|
ip = self.instance.nics[0].ip
|
4699 |
|
if self.bridge:
|
4700 |
|
bridge = self.bridge
|
4701 |
|
else:
|
4702 |
|
bridge = self.instance.nics[0].bridge
|
4703 |
|
if self.mac:
|
4704 |
|
mac = self.mac
|
4705 |
|
else:
|
4706 |
|
mac = self.instance.nics[0].mac
|
4707 |
|
args['nics'] = [(ip, bridge, mac)]
|
|
4788 |
# FIXME: readd disk/nic changes
|
4708 |
4789 |
env = _BuildInstanceHookEnvByObject(self, self.instance, override=args)
|
4709 |
4790 |
nl = [self.cfg.GetMasterNode(),
|
4710 |
4791 |
self.instance.primary_node] + list(self.instance.secondary_nodes)
|
... | ... | |
4716 |
4797 |
This only checks the instance list against the existing names.
|
4717 |
4798 |
|
4718 |
4799 |
"""
|
4719 |
|
# FIXME: all the parameters could be checked before, in ExpandNames, or in
|
4720 |
|
# a separate CheckArguments function, if we implement one, so the operation
|
4721 |
|
# can be aborted without waiting for any lock, should it have an error...
|
4722 |
|
self.ip = getattr(self.op, "ip", None)
|
4723 |
|
self.mac = getattr(self.op, "mac", None)
|
4724 |
|
self.bridge = getattr(self.op, "bridge", None)
|
4725 |
|
self.kernel_path = getattr(self.op, "kernel_path", None)
|
4726 |
|
self.initrd_path = getattr(self.op, "initrd_path", None)
|
4727 |
|
self.force = getattr(self.op, "force", None)
|
4728 |
|
all_parms = [self.ip, self.bridge, self.mac]
|
4729 |
|
if (all_parms.count(None) == len(all_parms) and
|
4730 |
|
not self.op.hvparams and
|
4731 |
|
not self.op.beparams):
|
4732 |
|
raise errors.OpPrereqError("No changes submitted")
|
4733 |
|
for item in (constants.BE_MEMORY, constants.BE_VCPUS):
|
4734 |
|
val = self.op.beparams.get(item, None)
|
4735 |
|
if val is not None:
|
4736 |
|
try:
|
4737 |
|
val = int(val)
|
4738 |
|
except ValueError, err:
|
4739 |
|
raise errors.OpPrereqError("Invalid %s size: %s" % (item, str(err)))
|
4740 |
|
self.op.beparams[item] = val
|
4741 |
|
if self.ip is not None:
|
4742 |
|
self.do_ip = True
|
4743 |
|
if self.ip.lower() == "none":
|
4744 |
|
self.ip = None
|
4745 |
|
else:
|
4746 |
|
if not utils.IsValidIP(self.ip):
|
4747 |
|
raise errors.OpPrereqError("Invalid IP address '%s'." % self.ip)
|
4748 |
|
else:
|
4749 |
|
self.do_ip = False
|
4750 |
|
self.do_bridge = (self.bridge is not None)
|
4751 |
|
if self.mac is not None:
|
4752 |
|
if self.cfg.IsMacInUse(self.mac):
|
4753 |
|
raise errors.OpPrereqError('MAC address %s already in use in cluster' %
|
4754 |
|
self.mac)
|
4755 |
|
if not utils.IsValidMac(self.mac):
|
4756 |
|
raise errors.OpPrereqError('Invalid MAC address %s' % self.mac)
|
|
4800 |
force = self.force = self.op.force
|
4757 |
4801 |
|
4758 |
4802 |
# checking the new params on the primary/secondary nodes
|
4759 |
4803 |
|
... | ... | |
4844 |
4888 |
self.warn.append("Not enough memory to failover instance to"
|
4845 |
4889 |
" secondary node %s" % node)
|
4846 |
4890 |
|
|
4891 |
# NIC processing
|
|
4892 |
for nic_op, nic_dict in self.op.nics:
|
|
4893 |
if nic_op == constants.DDM_REMOVE:
|
|
4894 |
if not instance.nics:
|
|
4895 |
raise errors.OpPrereqError("Instance has no NICs, cannot remove")
|
|
4896 |
continue
|
|
4897 |
if nic_op != constants.DDM_ADD:
|
|
4898 |
# an existing nic
|
|
4899 |
if nic_op < 0 or nic_op >= len(instance.nics):
|
|
4900 |
raise errors.OpPrereqError("Invalid NIC index %s, valid values"
|
|
4901 |
" are 0 to %d" %
|
|
4902 |
(nic_op, len(instance.nics)))
|
|
4903 |
nic_bridge = nic_dict.get('bridge', None)
|
|
4904 |
if nic_bridge is not None:
|
|
4905 |
if not self.rpc.call_bridges_exist(pnode, [nic_bridge]):
|
|
4906 |
msg = ("Bridge '%s' doesn't exist on one of"
|
|
4907 |
" the instance nodes" % nic_bridge)
|
|
4908 |
if self.force:
|
|
4909 |
self.warn.append(msg)
|
|
4910 |
else:
|
|
4911 |
raise errors.OpPrereqError(msg)
|
|
4912 |
|
|
4913 |
# DISK processing
|
|
4914 |
if self.op.disks and instance.disk_template == constants.DT_DISKLESS:
|
|
4915 |
raise errors.OpPrereqError("Disk operations not supported for"
|
|
4916 |
" diskless instances")
|
|
4917 |
for disk_op, disk_dict in self.op.disks:
|
|
4918 |
if disk_op == constants.DDM_REMOVE:
|
|
4919 |
if len(instance.disks) == 1:
|
|
4920 |
raise errors.OpPrereqError("Cannot remove the last disk of"
|
|
4921 |
" an instance")
|
|
4922 |
ins_l = self.rpc.call_instance_list([pnode], [instance.hypervisor])
|
|
4923 |
ins_l = ins_l[pnode]
|
|
4924 |
if not type(ins_l) is list:
|
|
4925 |
raise errors.OpPrereqError("Can't contact node '%s'" % pnode)
|
|
4926 |
if instance.name in ins_l:
|
|
4927 |
raise errors.OpPrereqError("Instance is running, can't remove"
|
|
4928 |
" disks.")
|
|
4929 |
|
|
4930 |
if (disk_op == constants.DDM_ADD and
|
|
4931 |
len(instance.nics) >= constants.MAX_DISKS):
|
|
4932 |
raise errors.OpPrereqError("Instance has too many disks (%d), cannot"
|
|
4933 |
" add more" % constants.MAX_DISKS)
|
|
4934 |
if disk_op not in (constants.DDM_ADD, constants.DDM_REMOVE):
|
|
4935 |
# an existing disk
|
|
4936 |
if disk_op < 0 or disk_op >= len(instance.disks):
|
|
4937 |
raise errors.OpPrereqError("Invalid disk index %s, valid values"
|
|
4938 |
" are 0 to %d" %
|
|
4939 |
(disk_op, len(instance.disks)))
|
|
4940 |
|
4847 |
4941 |
return
|
4848 |
4942 |
|
4849 |
4943 |
def Exec(self, feedback_fn):
|
4850 |
4944 |
"""Modifies an instance.
|
4851 |
4945 |
|
4852 |
4946 |
All parameters take effect only at the next restart of the instance.
|
|
4947 |
|
4853 |
4948 |
"""
|
4854 |
4949 |
# Process here the warnings from CheckPrereq, as we don't have a
|
4855 |
4950 |
# feedback_fn there.
|
... | ... | |
4858 |
4953 |
|
4859 |
4954 |
result = []
|
4860 |
4955 |
instance = self.instance
|
4861 |
|
if self.do_ip:
|
4862 |
|
instance.nics[0].ip = self.ip
|
4863 |
|
result.append(("ip", self.ip))
|
4864 |
|
if self.bridge:
|
4865 |
|
instance.nics[0].bridge = self.bridge
|
4866 |
|
result.append(("bridge", self.bridge))
|
4867 |
|
if self.mac:
|
4868 |
|
instance.nics[0].mac = self.mac
|
4869 |
|
result.append(("mac", self.mac))
|
|
4956 |
# disk changes
|
|
4957 |
for disk_op, disk_dict in self.op.disks:
|
|
4958 |
if disk_op == constants.DDM_REMOVE:
|
|
4959 |
# remove the last disk
|
|
4960 |
device = instance.disks.pop()
|
|
4961 |
device_idx = len(instance.disks)
|
|
4962 |
for node, disk in device.ComputeNodeTree(instance.primary_node):
|
|
4963 |
self.cfg.SetDiskID(disk, node)
|
|
4964 |
if not self.rpc.call_blockdev_remove(node, disk):
|
|
4965 |
self.proc.LogWarning("Could not remove disk/%d on node %s,"
|
|
4966 |
" continuing anyway", device_idx, node)
|
|
4967 |
result.append(("disk/%d" % device_idx, "remove"))
|
|
4968 |
elif disk_op == constants.DDM_ADD:
|
|
4969 |
# add a new disk
|
|
4970 |
if instance.disk_template == constants.DT_FILE:
|
|
4971 |
file_driver, file_path = instance.disks[0].logical_id
|
|
4972 |
file_path = os.path.dirname(file_path)
|
|
4973 |
else:
|
|
4974 |
file_driver = file_path = None
|
|
4975 |
disk_idx_base = len(instance.disks)
|
|
4976 |
new_disk = _GenerateDiskTemplate(self,
|
|
4977 |
instance.disk_template,
|
|
4978 |
instance, instance.primary_node,
|
|
4979 |
instance.secondary_nodes,
|
|
4980 |
[disk_dict],
|
|
4981 |
file_path,
|
|
4982 |
file_driver,
|
|
4983 |
disk_idx_base)[0]
|
|
4984 |
new_disk.mode = disk_dict['mode']
|
|
4985 |
instance.disks.append(new_disk)
|
|
4986 |
info = _GetInstanceInfoText(instance)
|
|
4987 |
|
|
4988 |
logging.info("Creating volume %s for instance %s",
|
|
4989 |
new_disk.iv_name, instance.name)
|
|
4990 |
# Note: this needs to be kept in sync with _CreateDisks
|
|
4991 |
#HARDCODE
|
|
4992 |
for secondary_node in instance.secondary_nodes:
|
|
4993 |
if not _CreateBlockDevOnSecondary(self, secondary_node, instance,
|
|
4994 |
new_disk, False, info):
|
|
4995 |
self.LogWarning("Failed to create volume %s (%s) on"
|
|
4996 |
" secondary node %s!",
|
|
4997 |
new_disk.iv_name, new_disk, secondary_node)
|
|
4998 |
#HARDCODE
|
|
4999 |
if not _CreateBlockDevOnPrimary(self, instance.primary_node,
|
|
5000 |
instance, new_disk, info):
|
|
5001 |
self.LogWarning("Failed to create volume %s on primary!",
|
|
5002 |
new_disk.iv_name)
|
|
5003 |
result.append(("disk/%d" % disk_idx_base, "add:size=%s,mode=%s" %
|
|
5004 |
(new_disk.size, new_disk.mode)))
|
|
5005 |
else:
|
|
5006 |
# change a given disk
|
|
5007 |
instance.disks[disk_op].mode = disk_dict['mode']
|
|
5008 |
result.append(("disk.mode/%d" % disk_op, disk_dict['mode']))
|
|
5009 |
# NIC changes
|
|
5010 |
for nic_op, nic_dict in self.op.nics:
|
|
5011 |
if nic_op == constants.DDM_REMOVE:
|
|
5012 |
# remove the last nic
|
|
5013 |
del instance.nics[-1]
|
|
5014 |
result.append(("nic.%d" % len(instance.nics), "remove"))
|
|
5015 |
elif nic_op == constants.DDM_ADD:
|
|
5016 |
# add a new nic
|
|
5017 |
if 'mac' not in nic_dict:
|
|
5018 |
mac = constants.VALUE_GENERATE
|
|
5019 |
else:
|
|
5020 |
mac = nic_dict['mac']
|
|
5021 |
if mac in (constants.VALUE_AUTO, constants.VALUE_GENERATE):
|
|
5022 |
mac = self.cfg.GenerateMAC()
|
|
5023 |
new_nic = objects.NIC(mac=mac, ip=nic_dict.get('ip', None),
|
|
5024 |
bridge=nic_dict.get('bridge', None))
|
|
5025 |
instance.nics.append(new_nic)
|
|
5026 |
result.append(("nic.%d" % (len(instance.nics) - 1),
|
|
5027 |
"add:mac=%s,ip=%s,bridge=%s" %
|
|
5028 |
(new_nic.mac, new_nic.ip, new_nic.bridge)))
|
|
5029 |
else:
|
|
5030 |
# change a given nic
|
|
5031 |
for key in 'mac', 'ip', 'bridge':
|
|
5032 |
if key in nic_dict:
|
|
5033 |
setattr(instance.nics[nic_op], key, nic_dict[key])
|
|
5034 |
result.append(("nic.%s/%d" % (key, nic_op), nic_dict[key]))
|
|
5035 |
|
|
5036 |
# hvparams changes
|
4870 |
5037 |
if self.op.hvparams:
|
4871 |
5038 |
instance.hvparams = self.hv_new
|
4872 |
5039 |
for key, val in self.op.hvparams.iteritems():
|
4873 |
5040 |
result.append(("hv/%s" % key, val))
|
|
5041 |
|
|
5042 |
# beparams changes
|
4874 |
5043 |
if self.op.beparams:
|
4875 |
5044 |
instance.beparams = self.be_inst
|
4876 |
5045 |
for key, val in self.op.beparams.iteritems():
|