Revision 24991749 lib/cmdlib.py
b/lib/cmdlib.py | ||
---|---|---|
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(): |
Also available in: Unified diff