4 # Copyright (C) 2013 Google Inc.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 from collections import defaultdict
28 from ganeti import compat
29 from ganeti import constants
30 from ganeti import objects
31 from ganeti import opcodes
32 from ganeti import errors
34 from testsupport import *
38 # pylint: disable=W0613
39 def _TcpPingFailSecondary(cfg, mock_fct, target, port, timeout=None,
40 live_port_needed=None, source=None):
41 # This will return True if target is in 192.0.2.0/24 (primary range)
43 return "192.0.2." in target
45 class TestLUNodeAdd(CmdlibTestCase):
47 super(TestLUNodeAdd, self).setUp()
49 # One node for testing readding:
50 self.node_readd = self.cfg.AddNewNode()
51 self.op_readd = opcodes.OpNodeAdd(node_name=self.node_readd.name,
53 primary_ip=self.node_readd.primary_ip,
54 secondary_ip=self.node_readd.secondary_ip)
56 # One node for testing adding:
57 # don't add to configuration now!
58 self.node_add = objects.Node(name="node_add",
59 primary_ip="192.0.2.200",
60 secondary_ip="203.0.113.200")
62 self.op_add = opcodes.OpNodeAdd(node_name=self.node_add.name,
63 primary_ip=self.node_add.primary_ip,
64 secondary_ip=self.node_add.secondary_ip)
66 self.netutils_mod.TcpPing.return_value = True
68 self.mocked_dns_rpc = self.rpc_mod.DnsOnlyRunner.return_value
70 self.mocked_dns_rpc.call_version.return_value = \
71 self.RpcResultsBuilder(use_node_names=True) \
72 .AddSuccessfulNode(self.node_add, constants.CONFIG_VERSION) \
73 .AddSuccessfulNode(self.node_readd, constants.CONFIG_VERSION) \
76 node_verify_result = \
77 self.RpcResultsBuilder() \
78 .CreateSuccessfulNodeResult(self.node_add, {constants.NV_NODELIST: []})
79 # we can't know the node's UUID in advance, so use defaultdict here
80 self.rpc.call_node_verify.return_value = \
81 defaultdict(lambda: node_verify_result, {})
83 def testOvsParamsButNotEnabled(self):
85 constants.ND_OVS: False,
86 constants.ND_OVS_NAME: "testswitch",
89 op = self.CopyOpCode(self.op_add,
92 self.ExecOpCodeExpectOpPrereqError(op, "OpenvSwitch is not enabled")
94 def testOvsNoLink(self):
96 constants.ND_OVS: True,
97 constants.ND_OVS_NAME: "testswitch",
98 constants.ND_OVS_LINK: None,
101 op = self.CopyOpCode(self.op_add,
105 self.assertLogContainsRegex(
106 "No physical interface for OpenvSwitch was given."
107 " OpenvSwitch will not have an outside connection."
108 " This might not be what you want")
110 created_node = self.cfg.GetNodeInfoByName(op.node_name)
111 self.assertEqual(ndparams[constants.ND_OVS],
112 created_node.ndparams.get(constants.ND_OVS, None))
113 self.assertEqual(ndparams[constants.ND_OVS_NAME],
114 created_node.ndparams.get(constants.ND_OVS_NAME, None))
115 self.assertEqual(ndparams[constants.ND_OVS_LINK],
116 created_node.ndparams.get(constants.ND_OVS_LINK, None))
118 def testWithoutOVS(self):
119 self.ExecOpCode(self.op_add)
121 created_node = self.cfg.GetNodeInfoByName(self.op_add.node_name)
122 self.assertEqual(None,
123 created_node.ndparams.get(constants.ND_OVS, None))
125 def testWithOVS(self):
127 constants.ND_OVS: True,
128 constants.ND_OVS_LINK: "eth2",
131 op = self.CopyOpCode(self.op_add,
136 created_node = self.cfg.GetNodeInfoByName(op.node_name)
137 self.assertEqual(ndparams[constants.ND_OVS],
138 created_node.ndparams.get(constants.ND_OVS, None))
139 self.assertEqual(ndparams[constants.ND_OVS_LINK],
140 created_node.ndparams.get(constants.ND_OVS_LINK, None))
142 def testReaddingMaster(self):
143 op = opcodes.OpNodeAdd(node_name=self.cfg.GetMasterNodeName(),
146 self.ExecOpCodeExpectOpPrereqError(op, "Cannot readd the master node")
148 def testReaddNotVmCapableNode(self):
149 self.cfg.AddNewInstance(primary_node=self.node_readd)
150 self.netutils_mod.GetHostname.return_value = \
151 HostnameMock(self.node_readd.name, self.node_readd.primary_ip)
153 op = self.CopyOpCode(self.op_readd, vm_capable=False)
155 self.ExecOpCodeExpectOpPrereqError(op, "Node .* being re-added with"
156 " vm_capable flag set to false, but it"
157 " already holds instances")
159 def testReaddAndPassNodeGroup(self):
160 op = self.CopyOpCode(self.op_readd,group="groupname")
162 self.ExecOpCodeExpectOpPrereqError(op, "Cannot pass a node group when a"
163 " node is being readded")
165 def testPrimaryIPv6(self):
166 self.master.secondary_ip = self.master.primary_ip
168 op = self.CopyOpCode(self.op_add, primary_ip="2001:DB8::1",
169 secondary_ip=self.REMOVE)
173 def testInvalidSecondaryIP(self):
174 op = self.CopyOpCode(self.op_add, secondary_ip="333.444.555.777")
176 self.ExecOpCodeExpectOpPrereqError(op, "Secondary IP .* needs to be a valid"
179 def testNodeAlreadyInCluster(self):
180 op = self.CopyOpCode(self.op_readd, readd=False)
182 self.ExecOpCodeExpectOpPrereqError(op, "Node %s is already in the"
183 " configuration" % self.node_readd.name)
185 def testReaddNodeNotInConfiguration(self):
186 op = self.CopyOpCode(self.op_add, readd=True)
188 self.ExecOpCodeExpectOpPrereqError(op, "Node %s is not in the"
189 " configuration" % self.node_add.name)
191 def testPrimaryIpConflict(self):
192 # In LUNodeAdd, DNS will resolve the node name to an IP address, that is
193 # used to overwrite any given primary_ip value!
194 # Thus we need to mock this DNS resolver here!
195 self.netutils_mod.GetHostname.return_value = \
196 HostnameMock(self.node_add.name, self.node_readd.primary_ip)
198 op = self.CopyOpCode(self.op_add)
200 self.ExecOpCodeExpectOpPrereqError(op, "New node ip address.* conflict with"
203 def testSecondaryIpConflict(self):
204 op = self.CopyOpCode(self.op_add, secondary_ip=self.node_readd.secondary_ip)
206 self.ExecOpCodeExpectOpPrereqError(op, "New node ip address.* conflict with"
209 def testReaddWithDifferentIP(self):
210 op = self.CopyOpCode(self.op_readd, primary_ip="192.0.2.100",
211 secondary_ip="230.0.113.100")
213 self.ExecOpCodeExpectOpPrereqError(op, "Readded node doesn't have the same"
214 " IP address configuration as before")
217 def testNodeHasSecondaryIpButNotMaster(self):
218 self.master.secondary_ip = self.master.primary_ip
220 self.ExecOpCodeExpectOpPrereqError(self.op_add, "The master has no"
221 " secondary ip but the new node has one")
223 def testMasterHasSecondaryIpButNotNode(self):
224 op = self.CopyOpCode(self.op_add, secondary_ip=None)
226 self.ExecOpCodeExpectOpPrereqError(op, "The master has a secondary ip but"
227 " the new node doesn't have one")
229 def testNodeNotReachableByPing(self):
230 self.netutils_mod.TcpPing.return_value = False
232 op = self.CopyOpCode(self.op_add)
234 self.ExecOpCodeExpectOpPrereqError(op, "Node not reachable by ping")
236 def testNodeNotReachableByPingOnSecondary(self):
237 self.netutils_mod.GetHostname.return_value = \
238 HostnameMock(self.node_add.name, self.node_add.primary_ip)
239 self.netutils_mod.TcpPing.side_effect = \
240 compat.partial(_TcpPingFailSecondary, self.cfg, self.netutils_mod.TcpPing)
242 op = self.CopyOpCode(self.op_add)
244 self.ExecOpCodeExpectOpPrereqError(op, "Node secondary ip not reachable by"
245 " TCP based ping to node daemon port")
247 def testCantGetVersion(self):
248 self.mocked_dns_rpc.call_version.return_value = \
249 self.RpcResultsBuilder(use_node_names=True) \
250 .AddErrorNode(self.node_add) \
253 op = self.CopyOpCode(self.op_add)
254 self.ExecOpCodeExpectOpExecError(op, "Can't get version information from"
255 " node %s" % self.node_add.name)
257 if __name__ == "__main__":
258 testutils.GanetiTestProgram()