root / test / ganeti.hypervisor.hv_kvm_unittest.py @ eea3b572
History | View | Annotate | Download (7.6 kB)
1 | 748e4b5a | Michael Hanselmann | #!/usr/bin/python
|
---|---|---|---|
2 | 748e4b5a | Michael Hanselmann | #
|
3 | 748e4b5a | Michael Hanselmann | |
4 | 55cc0a44 | Michael Hanselmann | # Copyright (C) 2010, 2011 Google Inc.
|
5 | 748e4b5a | Michael Hanselmann | #
|
6 | 748e4b5a | Michael Hanselmann | # This program is free software; you can redistribute it and/or modify
|
7 | 748e4b5a | Michael Hanselmann | # it under the terms of the GNU General Public License as published by
|
8 | 748e4b5a | Michael Hanselmann | # the Free Software Foundation; either version 2 of the License, or
|
9 | 748e4b5a | Michael Hanselmann | # (at your option) any later version.
|
10 | 748e4b5a | Michael Hanselmann | #
|
11 | 748e4b5a | Michael Hanselmann | # This program is distributed in the hope that it will be useful, but
|
12 | 748e4b5a | Michael Hanselmann | # WITHOUT ANY WARRANTY; without even the implied warranty of
|
13 | 748e4b5a | Michael Hanselmann | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
14 | 748e4b5a | Michael Hanselmann | # General Public License for more details.
|
15 | 748e4b5a | Michael Hanselmann | #
|
16 | 748e4b5a | Michael Hanselmann | # You should have received a copy of the GNU General Public License
|
17 | 748e4b5a | Michael Hanselmann | # along with this program; if not, write to the Free Software
|
18 | 748e4b5a | Michael Hanselmann | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
19 | 748e4b5a | Michael Hanselmann | # 02110-1301, USA.
|
20 | 748e4b5a | Michael Hanselmann | |
21 | 748e4b5a | Michael Hanselmann | |
22 | 748e4b5a | Michael Hanselmann | """Script for testing the hypervisor.hv_kvm module"""
|
23 | 748e4b5a | Michael Hanselmann | |
24 | 91c10532 | Andrea Spadaccini | import threading |
25 | 91c10532 | Andrea Spadaccini | import tempfile |
26 | 748e4b5a | Michael Hanselmann | import unittest |
27 | 91c10532 | Andrea Spadaccini | import socket |
28 | 91c10532 | Andrea Spadaccini | import os |
29 | 748e4b5a | Michael Hanselmann | |
30 | 91c10532 | Andrea Spadaccini | from ganeti import serializer |
31 | 748e4b5a | Michael Hanselmann | from ganeti import constants |
32 | 748e4b5a | Michael Hanselmann | from ganeti import compat |
33 | 748e4b5a | Michael Hanselmann | from ganeti import objects |
34 | 748e4b5a | Michael Hanselmann | from ganeti import errors |
35 | 748e4b5a | Michael Hanselmann | |
36 | 748e4b5a | Michael Hanselmann | from ganeti.hypervisor import hv_kvm |
37 | 748e4b5a | Michael Hanselmann | |
38 | 748e4b5a | Michael Hanselmann | import testutils |
39 | 748e4b5a | Michael Hanselmann | |
40 | 748e4b5a | Michael Hanselmann | |
41 | 91c10532 | Andrea Spadaccini | class QmpStub(threading.Thread): |
42 | 91c10532 | Andrea Spadaccini | """Stub for a QMP endpoint for a KVM instance
|
43 | 91c10532 | Andrea Spadaccini |
|
44 | 91c10532 | Andrea Spadaccini | """
|
45 | 51129a7f | Michael Hanselmann | _QMP_BANNER_DATA = { |
46 | 51129a7f | Michael Hanselmann | "QMP": {
|
47 | 51129a7f | Michael Hanselmann | "version": {
|
48 | 51129a7f | Michael Hanselmann | "package": "", |
49 | 51129a7f | Michael Hanselmann | "qemu": {
|
50 | 51129a7f | Michael Hanselmann | "micro": 50, |
51 | 51129a7f | Michael Hanselmann | "minor": 13, |
52 | 51129a7f | Michael Hanselmann | "major": 0, |
53 | 51129a7f | Michael Hanselmann | }, |
54 | 51129a7f | Michael Hanselmann | "capabilities": [],
|
55 | 51129a7f | Michael Hanselmann | }, |
56 | 51129a7f | Michael Hanselmann | } |
57 | 51129a7f | Michael Hanselmann | } |
58 | 51129a7f | Michael Hanselmann | _EMPTY_RESPONSE = { |
59 | 51129a7f | Michael Hanselmann | "return": [],
|
60 | 51129a7f | Michael Hanselmann | } |
61 | 91c10532 | Andrea Spadaccini | |
62 | 91c10532 | Andrea Spadaccini | def __init__(self, socket_filename, server_responses): |
63 | 91c10532 | Andrea Spadaccini | """Creates a QMP stub
|
64 | 91c10532 | Andrea Spadaccini |
|
65 | 91c10532 | Andrea Spadaccini | @type socket_filename: string
|
66 | 91c10532 | Andrea Spadaccini | @param socket_filename: filename of the UNIX socket that will be created
|
67 | 91c10532 | Andrea Spadaccini | this class and used for the communication
|
68 | 91c10532 | Andrea Spadaccini | @type server_responses: list
|
69 | 91c10532 | Andrea Spadaccini | @param server_responses: list of responses that the server sends in response
|
70 | 91c10532 | Andrea Spadaccini | to whatever it receives
|
71 | 91c10532 | Andrea Spadaccini | """
|
72 | 91c10532 | Andrea Spadaccini | threading.Thread.__init__(self)
|
73 | 91c10532 | Andrea Spadaccini | self.socket_filename = socket_filename
|
74 | 91c10532 | Andrea Spadaccini | self.script = server_responses
|
75 | 91c10532 | Andrea Spadaccini | |
76 | 91c10532 | Andrea Spadaccini | self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
77 | 91c10532 | Andrea Spadaccini | self.socket.bind(self.socket_filename) |
78 | 91c10532 | Andrea Spadaccini | self.socket.listen(1) |
79 | 91c10532 | Andrea Spadaccini | |
80 | 91c10532 | Andrea Spadaccini | def run(self): |
81 | 91c10532 | Andrea Spadaccini | # Hypothesis: the messages we receive contain only a complete QMP message
|
82 | 91c10532 | Andrea Spadaccini | # encoded in JSON.
|
83 | 91c10532 | Andrea Spadaccini | conn, addr = self.socket.accept()
|
84 | 91c10532 | Andrea Spadaccini | |
85 | 91c10532 | Andrea Spadaccini | # Send the banner as the first thing
|
86 | 91c10532 | Andrea Spadaccini | conn.send(self.encode_string(self._QMP_BANNER_DATA)) |
87 | 91c10532 | Andrea Spadaccini | |
88 | 91c10532 | Andrea Spadaccini | # Expect qmp_capabilities and return an empty response
|
89 | 91c10532 | Andrea Spadaccini | conn.recv(4096)
|
90 | 91c10532 | Andrea Spadaccini | conn.send(self.encode_string(self._EMPTY_RESPONSE)) |
91 | 91c10532 | Andrea Spadaccini | |
92 | 91c10532 | Andrea Spadaccini | while True: |
93 | 91c10532 | Andrea Spadaccini | # We ignore the expected message, as the purpose of this object is not
|
94 | 91c10532 | Andrea Spadaccini | # to verify the correctness of the communication but to act as a
|
95 | 91c10532 | Andrea Spadaccini | # partner for the SUT (System Under Test, that is QmpConnection)
|
96 | 91c10532 | Andrea Spadaccini | msg = conn.recv(4096)
|
97 | 91c10532 | Andrea Spadaccini | if not msg: |
98 | 91c10532 | Andrea Spadaccini | break
|
99 | 91c10532 | Andrea Spadaccini | |
100 | 91c10532 | Andrea Spadaccini | if not self.script: |
101 | 91c10532 | Andrea Spadaccini | break
|
102 | 91c10532 | Andrea Spadaccini | response = self.script.pop(0) |
103 | 91c10532 | Andrea Spadaccini | if isinstance(response, str): |
104 | 91c10532 | Andrea Spadaccini | conn.send(response) |
105 | 91c10532 | Andrea Spadaccini | elif isinstance(response, list): |
106 | 91c10532 | Andrea Spadaccini | for chunk in response: |
107 | 91c10532 | Andrea Spadaccini | conn.send(chunk) |
108 | 91c10532 | Andrea Spadaccini | else:
|
109 | 91c10532 | Andrea Spadaccini | raise errors.ProgrammerError("Unknown response type for %s" % response) |
110 | 91c10532 | Andrea Spadaccini | |
111 | 91c10532 | Andrea Spadaccini | conn.close() |
112 | 91c10532 | Andrea Spadaccini | |
113 | 91c10532 | Andrea Spadaccini | def encode_string(self, message): |
114 | a182a3ed | Michael Hanselmann | return (serializer.DumpJson(message) +
|
115 | 91c10532 | Andrea Spadaccini | hv_kvm.QmpConnection._MESSAGE_END_TOKEN) |
116 | 91c10532 | Andrea Spadaccini | |
117 | 91c10532 | Andrea Spadaccini | |
118 | 91c10532 | Andrea Spadaccini | class TestQmpMessage(testutils.GanetiTestCase): |
119 | 91c10532 | Andrea Spadaccini | def testSerialization(self): |
120 | 51129a7f | Michael Hanselmann | test_data = { |
121 | 51129a7f | Michael Hanselmann | "execute": "command", |
122 | 51129a7f | Michael Hanselmann | "arguments": ["a", "b", "c"], |
123 | 51129a7f | Michael Hanselmann | } |
124 | 91c10532 | Andrea Spadaccini | message = hv_kvm.QmpMessage(test_data) |
125 | 91c10532 | Andrea Spadaccini | |
126 | 91c10532 | Andrea Spadaccini | for k, v in test_data.items(): |
127 | 51129a7f | Michael Hanselmann | self.assertEqual(message[k], v)
|
128 | 91c10532 | Andrea Spadaccini | |
129 | 51129a7f | Michael Hanselmann | serialized = str(message)
|
130 | 51129a7f | Michael Hanselmann | self.assertEqual(len(serialized.splitlines()), 1, |
131 | 51129a7f | Michael Hanselmann | msg="Got multi-line message")
|
132 | 51129a7f | Michael Hanselmann | |
133 | 51129a7f | Michael Hanselmann | rebuilt_message = hv_kvm.QmpMessage.BuildFromJsonString(serialized) |
134 | 51129a7f | Michael Hanselmann | self.assertEqual(rebuilt_message, message)
|
135 | 91c10532 | Andrea Spadaccini | |
136 | 91c10532 | Andrea Spadaccini | |
137 | 91c10532 | Andrea Spadaccini | class TestQmp(testutils.GanetiTestCase): |
138 | 91c10532 | Andrea Spadaccini | def testQmp(self): |
139 | 91c10532 | Andrea Spadaccini | requests = [ |
140 | 91c10532 | Andrea Spadaccini | {"execute": "query-kvm", "arguments": []}, |
141 | 91c10532 | Andrea Spadaccini | {"execute": "eject", "arguments": {"device": "ide1-cd0"}}, |
142 | 91c10532 | Andrea Spadaccini | {"execute": "query-status", "arguments": []}, |
143 | 91c10532 | Andrea Spadaccini | {"execute": "query-name", "arguments": []}, |
144 | 91c10532 | Andrea Spadaccini | ] |
145 | 91c10532 | Andrea Spadaccini | |
146 | 91c10532 | Andrea Spadaccini | server_responses = [ |
147 | 91c10532 | Andrea Spadaccini | # One message, one send()
|
148 | 91c10532 | Andrea Spadaccini | '{"return": {"enabled": true, "present": true}}\r\n',
|
149 | 91c10532 | Andrea Spadaccini | |
150 | 91c10532 | Andrea Spadaccini | # Message sent using multiple send()
|
151 | 91c10532 | Andrea Spadaccini | ['{"retur', 'n": {}}\r\n'], |
152 | 91c10532 | Andrea Spadaccini | |
153 | 91c10532 | Andrea Spadaccini | # Multiple messages sent using one send()
|
154 | 91c10532 | Andrea Spadaccini | '{"return": [{"name": "quit"}, {"name": "eject"}]}\r\n'
|
155 | 91c10532 | Andrea Spadaccini | '{"return": {"running": true, "singlestep": false}}\r\n',
|
156 | 91c10532 | Andrea Spadaccini | ] |
157 | 91c10532 | Andrea Spadaccini | |
158 | 91c10532 | Andrea Spadaccini | expected_responses = [ |
159 | 91c10532 | Andrea Spadaccini | {"return": {"enabled": True, "present": True}}, |
160 | 91c10532 | Andrea Spadaccini | {"return": {}},
|
161 | 91c10532 | Andrea Spadaccini | {"return": [{"name": "quit"}, {"name": "eject"}]}, |
162 | 91c10532 | Andrea Spadaccini | {"return": {"running": True, "singlestep": False}}, |
163 | 91c10532 | Andrea Spadaccini | ] |
164 | 91c10532 | Andrea Spadaccini | |
165 | 91c10532 | Andrea Spadaccini | # Set up the stub
|
166 | 91c10532 | Andrea Spadaccini | socket_file = tempfile.NamedTemporaryFile() |
167 | 91c10532 | Andrea Spadaccini | os.remove(socket_file.name) |
168 | 91c10532 | Andrea Spadaccini | qmp_stub = QmpStub(socket_file.name, server_responses) |
169 | 91c10532 | Andrea Spadaccini | qmp_stub.start() |
170 | 91c10532 | Andrea Spadaccini | |
171 | 91c10532 | Andrea Spadaccini | # Set up the QMP connection
|
172 | 91c10532 | Andrea Spadaccini | qmp_connection = hv_kvm.QmpConnection(socket_file.name) |
173 | 91c10532 | Andrea Spadaccini | qmp_connection.connect() |
174 | 91c10532 | Andrea Spadaccini | |
175 | 91c10532 | Andrea Spadaccini | # Format the script
|
176 | 91c10532 | Andrea Spadaccini | for request, expected_response in zip(requests, expected_responses): |
177 | 91c10532 | Andrea Spadaccini | response = qmp_connection.Execute(request) |
178 | 51129a7f | Michael Hanselmann | msg = hv_kvm.QmpMessage(expected_response) |
179 | 51129a7f | Michael Hanselmann | self.assertEqual(len(str(msg).splitlines()), 1, |
180 | 51129a7f | Michael Hanselmann | msg="Got multi-line message")
|
181 | 51129a7f | Michael Hanselmann | self.assertEqual(response, msg)
|
182 | 91c10532 | Andrea Spadaccini | |
183 | 91c10532 | Andrea Spadaccini | |
184 | 55cc0a44 | Michael Hanselmann | class TestConsole(unittest.TestCase): |
185 | 55cc0a44 | Michael Hanselmann | def _Test(self, instance, hvparams): |
186 | 55cc0a44 | Michael Hanselmann | cons = hv_kvm.KVMHypervisor.GetInstanceConsole(instance, hvparams, {}) |
187 | 55cc0a44 | Michael Hanselmann | self.assertTrue(cons.Validate())
|
188 | 55cc0a44 | Michael Hanselmann | return cons
|
189 | 55cc0a44 | Michael Hanselmann | |
190 | 55cc0a44 | Michael Hanselmann | def testSerial(self): |
191 | 55cc0a44 | Michael Hanselmann | instance = objects.Instance(name="kvm.example.com",
|
192 | 55cc0a44 | Michael Hanselmann | primary_node="node6017")
|
193 | 55cc0a44 | Michael Hanselmann | hvparams = { |
194 | 55cc0a44 | Michael Hanselmann | constants.HV_SERIAL_CONSOLE: True,
|
195 | 55cc0a44 | Michael Hanselmann | constants.HV_VNC_BIND_ADDRESS: None,
|
196 | 4d2cdb5a | Andrea Spadaccini | constants.HV_KVM_SPICE_BIND: None,
|
197 | 55cc0a44 | Michael Hanselmann | } |
198 | 55cc0a44 | Michael Hanselmann | cons = self._Test(instance, hvparams)
|
199 | 55cc0a44 | Michael Hanselmann | self.assertEqual(cons.kind, constants.CONS_SSH)
|
200 | 55cc0a44 | Michael Hanselmann | self.assertEqual(cons.host, instance.primary_node)
|
201 | 2f4c951e | Stephen Shirley | self.assertEqual(cons.command[0], constants.KVM_CONSOLE_WRAPPER) |
202 | 2f4c951e | Stephen Shirley | self.assertEqual(cons.command[1], constants.SOCAT_PATH) |
203 | 55cc0a44 | Michael Hanselmann | |
204 | 55cc0a44 | Michael Hanselmann | def testVnc(self): |
205 | 55cc0a44 | Michael Hanselmann | instance = objects.Instance(name="kvm.example.com",
|
206 | 55cc0a44 | Michael Hanselmann | primary_node="node7235",
|
207 | 55cc0a44 | Michael Hanselmann | network_port=constants.VNC_BASE_PORT + 10)
|
208 | 55cc0a44 | Michael Hanselmann | hvparams = { |
209 | 55cc0a44 | Michael Hanselmann | constants.HV_SERIAL_CONSOLE: False,
|
210 | 55cc0a44 | Michael Hanselmann | constants.HV_VNC_BIND_ADDRESS: "192.0.2.1",
|
211 | 4d2cdb5a | Andrea Spadaccini | constants.HV_KVM_SPICE_BIND: None,
|
212 | 55cc0a44 | Michael Hanselmann | } |
213 | 55cc0a44 | Michael Hanselmann | cons = self._Test(instance, hvparams)
|
214 | 55cc0a44 | Michael Hanselmann | self.assertEqual(cons.kind, constants.CONS_VNC)
|
215 | 55cc0a44 | Michael Hanselmann | self.assertEqual(cons.host, "192.0.2.1") |
216 | 55cc0a44 | Michael Hanselmann | self.assertEqual(cons.port, constants.VNC_BASE_PORT + 10) |
217 | 55cc0a44 | Michael Hanselmann | self.assertEqual(cons.display, 10) |
218 | 55cc0a44 | Michael Hanselmann | |
219 | 4d2cdb5a | Andrea Spadaccini | def testSpice(self): |
220 | 4d2cdb5a | Andrea Spadaccini | instance = objects.Instance(name="kvm.example.com",
|
221 | 4d2cdb5a | Andrea Spadaccini | primary_node="node7235",
|
222 | 4d2cdb5a | Andrea Spadaccini | network_port=11000)
|
223 | 4d2cdb5a | Andrea Spadaccini | hvparams = { |
224 | 4d2cdb5a | Andrea Spadaccini | constants.HV_SERIAL_CONSOLE: False,
|
225 | 4d2cdb5a | Andrea Spadaccini | constants.HV_VNC_BIND_ADDRESS: None,
|
226 | 4d2cdb5a | Andrea Spadaccini | constants.HV_KVM_SPICE_BIND: "192.0.2.1",
|
227 | 4d2cdb5a | Andrea Spadaccini | } |
228 | 4d2cdb5a | Andrea Spadaccini | cons = self._Test(instance, hvparams)
|
229 | 4d2cdb5a | Andrea Spadaccini | self.assertEqual(cons.kind, constants.CONS_SPICE)
|
230 | 4d2cdb5a | Andrea Spadaccini | self.assertEqual(cons.host, "192.0.2.1") |
231 | 4d2cdb5a | Andrea Spadaccini | self.assertEqual(cons.port, 11000) |
232 | 4d2cdb5a | Andrea Spadaccini | |
233 | 55cc0a44 | Michael Hanselmann | def testNoConsole(self): |
234 | 55cc0a44 | Michael Hanselmann | instance = objects.Instance(name="kvm.example.com",
|
235 | 55cc0a44 | Michael Hanselmann | primary_node="node24325",
|
236 | 55cc0a44 | Michael Hanselmann | network_port=0)
|
237 | 55cc0a44 | Michael Hanselmann | hvparams = { |
238 | 55cc0a44 | Michael Hanselmann | constants.HV_SERIAL_CONSOLE: False,
|
239 | 55cc0a44 | Michael Hanselmann | constants.HV_VNC_BIND_ADDRESS: None,
|
240 | 4d2cdb5a | Andrea Spadaccini | constants.HV_KVM_SPICE_BIND: None,
|
241 | 55cc0a44 | Michael Hanselmann | } |
242 | 55cc0a44 | Michael Hanselmann | cons = self._Test(instance, hvparams)
|
243 | 55cc0a44 | Michael Hanselmann | self.assertEqual(cons.kind, constants.CONS_MESSAGE)
|
244 | 55cc0a44 | Michael Hanselmann | |
245 | 55cc0a44 | Michael Hanselmann | |
246 | 748e4b5a | Michael Hanselmann | if __name__ == "__main__": |
247 | 748e4b5a | Michael Hanselmann | testutils.GanetiTestProgram() |