diff --git a/lib/cmdlib.py b/lib/cmdlib.py index 5fb00488379dce87d66409b20331d2966337ed9c..972f141ddf9a5a57bc1c82b6b7e6bc6ac5197a27 100644 --- a/lib/cmdlib.py +++ b/lib/cmdlib.py @@ -7958,6 +7958,11 @@ def _RemoveDisks(lu, instance, target_node=None): " continuing anyway: %s", device.iv_name, node, msg) all_result = False + # if this is a DRBD disk, return its port to the pool + if device.dev_type in constants.LDS_DRBD: + tcp_port = device.logical_id[2] + lu.cfg.AddTcpUdpPort(tcp_port) + if instance.disk_template == constants.DT_FILE: file_storage_dir = os.path.dirname(instance.disks[0].logical_id[1]) if target_node: @@ -11139,6 +11144,11 @@ class LUInstanceSetParams(LogicalUnit): self.LogWarning("Could not remove metadata for disk %d on node %s," " continuing anyway: %s", idx, pnode, msg) + # this is a DRBD disk, return its port to the pool + for disk in old_disks: + tcp_port = disk.logical_id[2] + self.cfg.AddTcpUdpPort(tcp_port) + def Exec(self, feedback_fn): """Modifies an instance. @@ -11165,6 +11175,11 @@ class LUInstanceSetParams(LogicalUnit): self.LogWarning("Could not remove disk/%d on node %s: %s," " continuing anyway", device_idx, node, msg) result.append(("disk/%d" % device_idx, "remove")) + + # if this is a DRBD disk, return its port to the pool + if device.dev_type in constants.LDS_DRBD: + tcp_port = device.logical_id[2] + self.cfg.AddTcpUdpPort(tcp_port) elif disk_op == constants.DDM_ADD: # add a new disk if instance.disk_template in (constants.DT_FILE, diff --git a/lib/config.py b/lib/config.py index 11f69c269298a0dd9f1ca604c56044f1b5f23576..b61d9635a1b9e475d31b911b5e776fc6bdac64aa 100644 --- a/lib/config.py +++ b/lib/config.py @@ -1159,6 +1159,14 @@ class ConfigWriter: """ if instance_name not in self._config_data.instances: raise errors.ConfigurationError("Unknown instance '%s'" % instance_name) + + # If a network port has been allocated to the instance, + # return it to the pool of free ports. + inst = self._config_data.instances[instance_name] + network_port = getattr(inst, "network_port", None) + if network_port is not None: + self._config_data.cluster.tcpudp_port_pool.add(network_port) + del self._config_data.instances[instance_name] self._config_data.cluster.serial_no += 1 self._WriteConfig() diff --git a/lib/utils/text.py b/lib/utils/text.py index 7ec049e951a21b7717bcec91d5b3aa680fc5025d..f369c74f487cbeff0ab44ed671c66b7ea816986d 100644 --- a/lib/utils/text.py +++ b/lib/utils/text.py @@ -383,10 +383,12 @@ def UnescapeAndSplit(text, sep=","): num_b = len(e1) - len(e1.rstrip("\\")) if num_b % 2 == 1 and slist: e2 = slist.pop(0) - # here the backslashes remain (all), and will be reduced in - # the next step - rlist.append(e1 + sep + e2) + # Merge the two elements and push the result back to the source list for + # revisiting. If e2 ended with backslashes, further merging may need to + # be done. + slist.insert(0, e1 + sep + e2) continue + # here the backslashes remain (all), and will be reduced in the next step rlist.append(e1) # finally, replace backslash-something with something rlist = [re.sub(r"\\(.)", r"\1", v) for v in rlist] diff --git a/test/ganeti.utils.text_unittest.py b/test/ganeti.utils.text_unittest.py index 91039e173a765a1bbb6eddc09edda95b4d651d7c..03c8d64a0f25685688acc8f70b48b9f564f68940 100755 --- a/test/ganeti.utils.text_unittest.py +++ b/test/ganeti.utils.text_unittest.py @@ -387,6 +387,12 @@ class TestUnescapeAndSplit(unittest.TestCase): b = [sep, sep, "c", "d.moo\\"] self.assertEqual(utils.UnescapeAndSplit("%s\\" % sep.join(a), sep=sep), b) + def testMultipleEscapes(self): + for sep in self._seps: + a = ["a", "b\\" + sep + "c", "d\\" + sep + "e\\" + sep + "f", "g"] + b = ["a", "b" + sep + "c", "d" + sep + "e" + sep + "f", "g"] + self.failUnlessEqual(utils.UnescapeAndSplit(sep.join(a), sep=sep), b) + class TestCommaJoin(unittest.TestCase): def test(self):