diff --git a/lib/cmdlib.py b/lib/cmdlib.py index 9e095839ba480745eca940e387aa38b855513b0e..1995b2ba882df760eadc3fc49fc5583aef73a39f 100644 --- a/lib/cmdlib.py +++ b/lib/cmdlib.py @@ -10186,6 +10186,11 @@ class LUInstanceCreate(LogicalUnit): # check nics' parameter names for nic in self.op.nics: utils.ForceDictType(nic, constants.INIC_PARAMS_TYPES) + # check that NIC's parameters names are unique and valid + utils.ValidateDeviceNames("NIC", self.op.nics) + + # check that disk's names are unique and valid + utils.ValidateDeviceNames("disk", self.op.disks) cluster = self.cfg.GetClusterInfo() if not self.op.disk_template in cluster.enabled_disk_templates: @@ -14009,9 +14014,14 @@ class LUInstanceSetParams(LogicalUnit): " (%d), cannot add more" % constants.MAX_NICS, errors.ECODE_STATE) + def _PrepareDiskMod(_, disk, params, __): + disk.name = params.get(constants.IDISK_NAME, None) + # Verify disk changes (operating on a copy) - disks = instance.disks[:] - ApplyContainerMods("disk", disks, None, self.diskmod, None, None, None) + disks = copy.deepcopy(instance.disks) + ApplyContainerMods("disk", disks, None, self.diskmod, None, _PrepareDiskMod, + None) + utils.ValidateDeviceNames("disk", disks) if len(disks) > constants.MAX_DISKS: raise errors.OpPrereqError("Instance has too many disks (%d), cannot add" " more" % constants.MAX_DISKS, @@ -14033,6 +14043,8 @@ class LUInstanceSetParams(LogicalUnit): nics = [nic.Copy() for nic in instance.nics] ApplyContainerMods("NIC", nics, self._nic_chgdesc, self.nicmod, self._CreateNewNic, self._ApplyNicMods, None) + # Verify that NIC names are unique and valid + utils.ValidateDeviceNames("NIC", nics) self._new_nics = nics ispec[constants.ISPEC_NIC_COUNT] = len(self._new_nics) else: diff --git a/lib/utils/__init__.py b/lib/utils/__init__.py index d3319c1e2fafa4221f418985225de9d8874eaa95..cda87f4547938fd73591e00195789bd57e0518f7 100644 --- a/lib/utils/__init__.py +++ b/lib/utils/__init__.py @@ -797,3 +797,47 @@ class FieldSet(object): """ return [val for val in items if not self.Matches(val)] + + +def ValidateDeviceNames(kind, container): + """Validate instance device names. + + Check that a device container contains only unique and valid names. + + @type kind: string + @param kind: One-word item description + @type container: list + @param container: Container containing the devices + + """ + + valid = [] + for device in container: + if isinstance(device, dict): + if kind == "NIC": + name = device.get(constants.INIC_NAME, None) + elif kind == "disk": + name = device.get(constants.IDISK_NAME, None) + else: + raise errors.OpPrereqError("Invalid container kind '%s'" % kind, + errors.ECODE_INVAL) + else: + name = device.name + # Check that a device name is not the UUID of another device + valid.append(device.uuid) + + try: + int(name) + except (ValueError, TypeError): + pass + else: + raise errors.OpPrereqError("Invalid name '%s'. Purely numeric %s names" + " are not allowed" % (name, kind), + errors.ECODE_INVAL) + + if name is not None and name.lower() != constants.VALUE_NONE: + if name in valid: + raise errors.OpPrereqError("%s name '%s' already used" % (kind, name), + errors.ECODE_NOTUNIQUE) + else: + valid.append(name) diff --git a/test/py/ganeti.utils_unittest.py b/test/py/ganeti.utils_unittest.py index 343e029fb88280bb9835e9ffa7602b4c38dfdd83..f57b713ba46552ea991cc523cf6f435d74f19303 100755 --- a/test/py/ganeti.utils_unittest.py +++ b/test/py/ganeti.utils_unittest.py @@ -369,5 +369,26 @@ class TestVerifyDictOptions(unittest.TestCase): some_keys, self.defaults) +class TestValidateDeviceNames(unittest.TestCase): + def testEmpty(self): + utils.ValidateDeviceNames("NIC", []) + utils.ValidateDeviceNames("disk", []) + + def testNoName(self): + nics = [{}, {}] + utils.ValidateDeviceNames("NIC", nics) + + def testInvalidName(self): + self.assertRaises(errors.OpPrereqError, utils.ValidateDeviceNames, + "disk", [{constants.IDISK_NAME: "42"}]) + self.assertRaises(errors.OpPrereqError, utils.ValidateDeviceNames, + "NIC", [{constants.INIC_NAME: "42"}]) + + def testUsedName(self): + disks = [{constants.IDISK_NAME: "name1"}, {constants.IDISK_NAME: "name1"}] + self.assertRaises(errors.OpPrereqError, utils.ValidateDeviceNames, + "disk", disks) + + if __name__ == "__main__": testutils.GanetiTestProgram()