diff --git a/lib/masterd/iallocator.py b/lib/masterd/iallocator.py index 2db8ac4b8fa6c60ab9ca0469441d8c3eb80c5441..45b0ba1b538806741f5218cf3fce4802a164a1e6 100644 --- a/lib/masterd/iallocator.py +++ b/lib/masterd/iallocator.py @@ -153,6 +153,7 @@ class IAReqInstanceAlloc(IARequestBase): ("nics", ht.TListOf(ht.TDict)), ("vcpus", ht.TInt), ("hypervisor", ht.TString), + ("node_whitelist", ht.TMaybeListOf(ht.TNonEmptyString)), ] REQ_RESULT = ht.TList @@ -421,10 +422,13 @@ class IAllocator(object): if isinstance(self.req, IAReqInstanceAlloc): hypervisor_name = self.req.hypervisor + node_whitelist = self.req.node_whitelist elif isinstance(self.req, IAReqRelocate): hypervisor_name = cfg.GetInstanceInfo(self.req.name).hypervisor + node_whitelist = None else: hypervisor_name = cluster_info.primary_hypervisor + node_whitelist = None node_data = self.rpc.call_node_info(node_list, [cfg.GetVGName()], [hypervisor_name]) @@ -434,7 +438,7 @@ class IAllocator(object): data["nodegroups"] = self._ComputeNodeGroupData(cfg) - config_ndata = self._ComputeBasicNodeData(cfg, ninfo) + config_ndata = self._ComputeBasicNodeData(cfg, ninfo, node_whitelist) data["nodes"] = self._ComputeDynamicNodeData(ninfo, node_data, node_iinfo, i_list, config_ndata) assert len(data["nodes"]) == len(ninfo), \ @@ -461,7 +465,7 @@ class IAllocator(object): return ng @staticmethod - def _ComputeBasicNodeData(cfg, node_cfg): + def _ComputeBasicNodeData(cfg, node_cfg, node_whitelist): """Compute global node data. @rtype: dict @@ -473,7 +477,9 @@ class IAllocator(object): "tags": list(ninfo.GetTags()), "primary_ip": ninfo.primary_ip, "secondary_ip": ninfo.secondary_ip, - "offline": ninfo.offline, + "offline": (ninfo.offline or + not (node_whitelist is None or + ninfo.name in node_whitelist)), "drained": ninfo.drained, "master_candidate": ninfo.master_candidate, "group": ninfo.group, diff --git a/test/ganeti.masterd.iallocator_unittest.py b/test/ganeti.masterd.iallocator_unittest.py index 8aa2bd05bf6d2108ec35203f446a096addee1edb..85b88a9af16d67b1a3d2f14c1ebbf09f0042e8b0 100755 --- a/test/ganeti.masterd.iallocator_unittest.py +++ b/test/ganeti.masterd.iallocator_unittest.py @@ -26,6 +26,7 @@ import unittest from ganeti import compat from ganeti import constants from ganeti import errors +from ganeti import objects from ganeti import ht from ganeti.masterd import iallocator @@ -88,5 +89,94 @@ class TestIARequestBase(unittest.TestCase): stub.ValidateResult(_StubIAllocator(False), "foo") +class _FakeConfigWithNdParams: + def GetNdParams(self, _): + return None + + +class TestComputeBasicNodeData(unittest.TestCase): + def setUp(self): + self.fn = compat.partial(iallocator.IAllocator._ComputeBasicNodeData, + _FakeConfigWithNdParams()) + + def testEmpty(self): + self.assertEqual(self.fn({}, None), {}) + + def testSimple(self): + node1 = objects.Node(name="node1", + primary_ip="192.0.2.1", + secondary_ip="192.0.2.2", + offline=False, + drained=False, + master_candidate=True, + master_capable=True, + group="11112222", + vm_capable=False) + + node2 = objects.Node(name="node2", + primary_ip="192.0.2.3", + secondary_ip="192.0.2.4", + offline=True, + drained=False, + master_candidate=False, + master_capable=False, + group="11112222", + vm_capable=True) + + assert node1 != node2 + + ninfo = { + "#unused-1#": node1, + "#unused-2#": node2, + } + + self.assertEqual(self.fn(ninfo, None), { + "node1": { + "tags": [], + "primary_ip": "192.0.2.1", + "secondary_ip": "192.0.2.2", + "offline": False, + "drained": False, + "master_candidate": True, + "group": "11112222", + "master_capable": True, + "vm_capable": False, + "ndparams": None, + }, + "node2": { + "tags": [], + "primary_ip": "192.0.2.3", + "secondary_ip": "192.0.2.4", + "offline": True, + "drained": False, + "master_candidate": False, + "group": "11112222", + "master_capable": False, + "vm_capable": True, + "ndparams": None, + }, + }) + + def testOfflineNode(self): + for whitelist in [None, [], set(), ["node1"], ["node2"]]: + result = self.fn({ + "node1": objects.Node(name="node1", offline=True) + }, whitelist) + self.assertEqual(len(result), 1) + self.assertTrue(result["node1"]["offline"]) + + def testWhitelist(self): + for whitelist in [None, [], set(), ["node1"], ["node2"]]: + result = self.fn({ + "node1": objects.Node(name="node1", offline=False) + }, whitelist) + self.assertEqual(len(result), 1) + + if whitelist is None or "node1" in whitelist: + self.assertFalse(result["node1"]["offline"]) + else: + self.assertTrue(result["node1"]["offline"]) + + if __name__ == "__main__": testutils.GanetiTestProgram()