Merge branch 'stable-2.9' into stable-2.10
[ganeti-local] / test / py / cmdlib / node_unittest.py
1 #!/usr/bin/python
2 #
3
4 # Copyright (C) 2013 Google Inc.
5 #
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.
10 #
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.
15 #
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
19 # 02110-1301, USA.
20
21
22 """Tests for LUNode*
23
24 """
25
26 from collections import defaultdict
27
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
33
34 from testsupport import *
35
36 import testutils
37
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)
42   # and False if not.
43   return "192.0.2." in target
44
45 class TestLUNodeAdd(CmdlibTestCase):
46   def setUp(self):
47     super(TestLUNodeAdd, self).setUp()
48
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,
52                                       readd=True,
53                                       primary_ip=self.node_readd.primary_ip,
54                                       secondary_ip=self.node_readd.secondary_ip)
55
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")
61
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)
65
66     self.netutils_mod.TcpPing.return_value = True
67
68     self.mocked_dns_rpc = self.rpc_mod.DnsOnlyRunner.return_value
69
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) \
74         .Build()
75
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, {})
82
83   def testOvsParamsButNotEnabled(self):
84     ndparams = {
85       constants.ND_OVS: False,
86       constants.ND_OVS_NAME: "testswitch",
87     }
88
89     op = self.CopyOpCode(self.op_add,
90                          ndparams=ndparams)
91
92     self.ExecOpCodeExpectOpPrereqError(op, "OpenvSwitch is not enabled")
93
94   def testOvsNoLink(self):
95     ndparams = {
96       constants.ND_OVS: True,
97       constants.ND_OVS_NAME: "testswitch",
98       constants.ND_OVS_LINK: None,
99     }
100
101     op = self.CopyOpCode(self.op_add,
102                          ndparams=ndparams)
103
104     self.ExecOpCode(op)
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")
109
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))
117
118   def testWithoutOVS(self):
119     self.ExecOpCode(self.op_add)
120
121     created_node = self.cfg.GetNodeInfoByName(self.op_add.node_name)
122     self.assertEqual(None,
123                      created_node.ndparams.get(constants.ND_OVS, None))
124
125   def testWithOVS(self):
126     ndparams = {
127       constants.ND_OVS: True,
128       constants.ND_OVS_LINK: "eth2",
129     }
130
131     op = self.CopyOpCode(self.op_add,
132                          ndparams=ndparams)
133
134     self.ExecOpCode(op)
135
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))
141
142   def testReaddingMaster(self):
143     op = opcodes.OpNodeAdd(node_name=self.cfg.GetMasterNodeName(),
144                            readd=True)
145
146     self.ExecOpCodeExpectOpPrereqError(op, "Cannot readd the master node")
147
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)
152
153     op = self.CopyOpCode(self.op_readd, vm_capable=False)
154
155     self.ExecOpCodeExpectOpPrereqError(op, "Node .* being re-added with"
156                                        " vm_capable flag set to false, but it"
157                                        " already holds instances")
158
159   def testReaddAndPassNodeGroup(self):
160     op = self.CopyOpCode(self.op_readd,group="groupname")
161
162     self.ExecOpCodeExpectOpPrereqError(op, "Cannot pass a node group when a"
163                                        " node is being readded")
164
165   def testPrimaryIPv6(self):
166     self.master.secondary_ip = self.master.primary_ip
167
168     op = self.CopyOpCode(self.op_add, primary_ip="2001:DB8::1",
169                          secondary_ip=self.REMOVE)
170
171     self.ExecOpCode(op)
172
173   def testInvalidSecondaryIP(self):
174     op = self.CopyOpCode(self.op_add, secondary_ip="333.444.555.777")
175
176     self.ExecOpCodeExpectOpPrereqError(op, "Secondary IP .* needs to be a valid"
177                                        " IPv4 address")
178
179   def testNodeAlreadyInCluster(self):
180     op = self.CopyOpCode(self.op_readd, readd=False)
181
182     self.ExecOpCodeExpectOpPrereqError(op, "Node %s is already in the"
183                                        " configuration" % self.node_readd.name)
184
185   def testReaddNodeNotInConfiguration(self):
186     op = self.CopyOpCode(self.op_add, readd=True)
187
188     self.ExecOpCodeExpectOpPrereqError(op, "Node %s is not in the"
189                                        " configuration" % self.node_add.name)
190
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)
197
198     op = self.CopyOpCode(self.op_add)
199
200     self.ExecOpCodeExpectOpPrereqError(op, "New node ip address.* conflict with"
201                                        " existing node")
202
203   def testSecondaryIpConflict(self):
204     op = self.CopyOpCode(self.op_add, secondary_ip=self.node_readd.secondary_ip)
205
206     self.ExecOpCodeExpectOpPrereqError(op, "New node ip address.* conflict with"
207                                        " existing node")
208
209   def testReaddWithDifferentIP(self):
210     op = self.CopyOpCode(self.op_readd, primary_ip="192.0.2.100",
211                          secondary_ip="230.0.113.100")
212
213     self.ExecOpCodeExpectOpPrereqError(op, "Readded node doesn't have the same"
214                                        " IP address configuration as before")
215
216
217   def testNodeHasSecondaryIpButNotMaster(self):
218     self.master.secondary_ip = self.master.primary_ip
219
220     self.ExecOpCodeExpectOpPrereqError(self.op_add, "The master has no"
221                                        " secondary ip but the new node has one")
222
223   def testMasterHasSecondaryIpButNotNode(self):
224     op = self.CopyOpCode(self.op_add, secondary_ip=None)
225
226     self.ExecOpCodeExpectOpPrereqError(op, "The master has a secondary ip but"
227                                        " the new node doesn't have one")
228
229   def testNodeNotReachableByPing(self):
230     self.netutils_mod.TcpPing.return_value = False
231
232     op = self.CopyOpCode(self.op_add)
233
234     self.ExecOpCodeExpectOpPrereqError(op, "Node not reachable by ping")
235
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)
241
242     op = self.CopyOpCode(self.op_add)
243
244     self.ExecOpCodeExpectOpPrereqError(op, "Node secondary ip not reachable by"
245                                        " TCP based ping to node daemon port")
246
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) \
251         .Build()
252
253     op = self.CopyOpCode(self.op_add)
254     self.ExecOpCodeExpectOpExecError(op, "Can't get version information from"
255                                      " node %s" % self.node_add.name)
256
257 if __name__ == "__main__":
258   testutils.GanetiTestProgram()