Merge branch 'stable-2.6-esi' into stable-2.6-ippool-hotplug-esi
[ganeti-local] / test / ganeti.confd.client_unittest.py
1 #!/usr/bin/python
2 #
3
4 # Copyright (C) 2009 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 """Script for unittesting the confd client module"""
23
24
25 import socket
26 import unittest
27
28 from ganeti import confd
29 from ganeti import constants
30 from ganeti import errors
31
32 import ganeti.confd.client
33
34 import testutils
35
36
37 class ResettableMock(object):
38   def __init__(self, *args, **kwargs):
39     self.Reset()
40
41   def Reset(self):
42     pass
43
44
45 class MockLogger(ResettableMock):
46   def Reset(self):
47     self.debug_count = 0
48     self.warn_count = 0
49     self.error_count = 0
50
51   def debug(string):
52     self.debug_count += 1
53
54   def warning(string):
55     self.warn_count += 1
56
57   def error(string):
58     self.error_count += 1
59
60 class MockConfdAsyncUDPClient(ResettableMock):
61   def Reset(self):
62     self.send_count = 0
63     self.last_address = ''
64     self.last_port = -1
65     self.last_sent = ''
66
67   def enqueue_send(self, address, port, payload):
68     self.send_count += 1
69     self.last_payload = payload
70     self.last_port = port
71     self.last_address = address
72
73 class MockCallback(ResettableMock):
74   def Reset(self):
75     self.call_count = 0
76     self.last_up = None
77
78   def __call__(self, up):
79     """Callback
80
81     @type up: L{ConfdUpcallPayload}
82     @param up: upper callback
83
84     """
85     self.call_count += 1
86     self.last_up = up
87
88
89 class MockTime(ResettableMock):
90   def Reset(self):
91     self.mytime  = 1254213006.5175071
92
93   def time(self):
94     return self.mytime
95
96   def increase(self, delta):
97     self.mytime += delta
98
99
100 class _BaseClientTest:
101   """Base class for client tests"""
102   mc_list = None
103   new_peers = None
104   family = None
105
106   def setUp(self):
107     self.mock_time = MockTime()
108     confd.client.time = self.mock_time
109     confd.client.ConfdAsyncUDPClient = MockConfdAsyncUDPClient
110     self.logger = MockLogger()
111     hmac_key = "mykeydata"
112     self.callback = MockCallback()
113     self.client = confd.client.ConfdClient(hmac_key, self.mc_list,
114                                            self.callback, logger=self.logger)
115
116   def testRequest(self):
117     req1 = confd.client.ConfdClientRequest(type=constants.CONFD_REQ_PING)
118     req2 = confd.client.ConfdClientRequest(type=constants.CONFD_REQ_PING)
119     self.assertNotEqual(req1.rsalt, req2.rsalt)
120     self.assertEqual(req1.protocol, constants.CONFD_PROTOCOL_VERSION)
121     self.assertEqual(req2.protocol, constants.CONFD_PROTOCOL_VERSION)
122     self.assertRaises(errors.ConfdClientError, confd.client.ConfdClientRequest,
123                       type=-33)
124
125   def testClientSend(self):
126     req = confd.client.ConfdClientRequest(type=constants.CONFD_REQ_PING)
127     self.client.SendRequest(req)
128     # Cannot send the same request twice
129     self.assertRaises(errors.ConfdClientError, self.client.SendRequest, req)
130     req2 = confd.client.ConfdClientRequest(type=constants.CONFD_REQ_PING)
131     # Coverage is too big
132     self.assertRaises(errors.ConfdClientError, self.client.SendRequest,
133                       req2, coverage=15)
134     self.assertEquals(self.client._socket.send_count,
135                       constants.CONFD_DEFAULT_REQ_COVERAGE)
136     # Send with max coverage
137     self.client.SendRequest(req2, coverage=-1)
138     self.assertEquals(self.client._socket.send_count,
139                       constants.CONFD_DEFAULT_REQ_COVERAGE + len(self.mc_list))
140     self.assert_(self.client._socket.last_address in self.mc_list)
141
142
143   def testClientExpire(self):
144     req = confd.client.ConfdClientRequest(type=constants.CONFD_REQ_PING)
145     self.client.SendRequest(req)
146     # Make a couple of seconds pass ;)
147     self.mock_time.increase(2)
148     # Now sending the second request
149     req2 = confd.client.ConfdClientRequest(type=constants.CONFD_REQ_PING)
150     self.client.SendRequest(req2)
151     self.mock_time.increase(constants.CONFD_CLIENT_EXPIRE_TIMEOUT - 1)
152     # First request should be expired, second one should not
153     self.client.ExpireRequests()
154     self.assertEquals(self.callback.call_count, 1)
155     self.assertEquals(self.callback.last_up.type, confd.client.UPCALL_EXPIRE)
156     self.assertEquals(self.callback.last_up.salt, req.rsalt)
157     self.assertEquals(self.callback.last_up.orig_request, req)
158     self.mock_time.increase(3)
159     self.assertEquals(self.callback.call_count, 1)
160     self.client.ExpireRequests()
161     self.assertEquals(self.callback.call_count, 2)
162     self.assertEquals(self.callback.last_up.type, confd.client.UPCALL_EXPIRE)
163     self.assertEquals(self.callback.last_up.salt, req2.rsalt)
164     self.assertEquals(self.callback.last_up.orig_request, req2)
165
166   def testClientCascadeExpire(self):
167     req = confd.client.ConfdClientRequest(type=constants.CONFD_REQ_PING)
168     self.client.SendRequest(req)
169     self.mock_time.increase(constants.CONFD_CLIENT_EXPIRE_TIMEOUT +1)
170     req2 = confd.client.ConfdClientRequest(type=constants.CONFD_REQ_PING)
171     self.client.SendRequest(req2)
172     self.assertEquals(self.callback.call_count, 1)
173
174   def testUpdatePeerList(self):
175     self.client.UpdatePeerList(self.new_peers)
176     self.assertEquals(self.client._peers, self.new_peers)
177     req = confd.client.ConfdClientRequest(type=constants.CONFD_REQ_PING)
178     self.client.SendRequest(req)
179     self.assertEquals(self.client._socket.send_count, len(self.new_peers))
180     self.assert_(self.client._socket.last_address in self.new_peers)
181
182   def testSetPeersFamily(self):
183     self.client._SetPeersAddressFamily()
184     self.assertEquals(self.client._family, self.family)
185     mixed_peers = ["192.0.2.99", "2001:db8:beef::13"]
186     self.client.UpdatePeerList(mixed_peers)
187     self.assertRaises(errors.ConfdClientError,
188                       self.client._SetPeersAddressFamily)
189
190
191 class TestIP4Client(unittest.TestCase, _BaseClientTest):
192   """Client tests"""
193   mc_list = ["192.0.2.1",
194              "192.0.2.2",
195              "192.0.2.3",
196              "192.0.2.4",
197              "192.0.2.5",
198              "192.0.2.6",
199              "192.0.2.7",
200              "192.0.2.8",
201              "192.0.2.9",
202             ]
203   new_peers = ["198.51.100.1", "198.51.100.2"]
204   family = socket.AF_INET
205
206   def setUp(self):
207     unittest.TestCase.setUp(self)
208     _BaseClientTest.setUp(self)
209
210
211 class TestIP6Client(unittest.TestCase, _BaseClientTest):
212   """Client tests"""
213   mc_list = ["2001:db8::1",
214              "2001:db8::2",
215              "2001:db8::3",
216              "2001:db8::4",
217              "2001:db8::5",
218              "2001:db8::6",
219              "2001:db8::7",
220              "2001:db8::8",
221              "2001:db8::9",
222             ]
223   new_peers = ["2001:db8:beef::11", "2001:db8:beef::12"]
224   family = socket.AF_INET6
225
226   def setUp(self):
227     unittest.TestCase.setUp(self)
228     _BaseClientTest.setUp(self)
229
230
231 if __name__ == '__main__':
232   testutils.GanetiTestProgram()