root / test / ganeti.hypervisor.hv_kvm_unittest.py @ 42d4d8b9
History | View | Annotate | Download (7.4 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 | 91c10532 | Andrea Spadaccini | _QMP_BANNER_DATA = {"QMP": {"version": { |
46 | 91c10532 | Andrea Spadaccini | "package": "", |
47 | 91c10532 | Andrea Spadaccini | "qemu": {"micro": 50, "minor": 13, "major": 0}, |
48 | 91c10532 | Andrea Spadaccini | "capabilities": [],
|
49 | 91c10532 | Andrea Spadaccini | }}} |
50 | 91c10532 | Andrea Spadaccini | _EMPTY_RESPONSE = {"return": []}
|
51 | 91c10532 | Andrea Spadaccini | |
52 | 91c10532 | Andrea Spadaccini | def __init__(self, socket_filename, server_responses): |
53 | 91c10532 | Andrea Spadaccini | """Creates a QMP stub
|
54 | 91c10532 | Andrea Spadaccini |
|
55 | 91c10532 | Andrea Spadaccini | @type socket_filename: string
|
56 | 91c10532 | Andrea Spadaccini | @param socket_filename: filename of the UNIX socket that will be created
|
57 | 91c10532 | Andrea Spadaccini | this class and used for the communication
|
58 | 91c10532 | Andrea Spadaccini | @type server_responses: list
|
59 | 91c10532 | Andrea Spadaccini | @param server_responses: list of responses that the server sends in response
|
60 | 91c10532 | Andrea Spadaccini | to whatever it receives
|
61 | 91c10532 | Andrea Spadaccini | """
|
62 | 91c10532 | Andrea Spadaccini | threading.Thread.__init__(self)
|
63 | 91c10532 | Andrea Spadaccini | self.socket_filename = socket_filename
|
64 | 91c10532 | Andrea Spadaccini | self.script = server_responses
|
65 | 91c10532 | Andrea Spadaccini | |
66 | 91c10532 | Andrea Spadaccini | self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
67 | 91c10532 | Andrea Spadaccini | self.socket.bind(self.socket_filename) |
68 | 91c10532 | Andrea Spadaccini | self.socket.listen(1) |
69 | 91c10532 | Andrea Spadaccini | |
70 | 91c10532 | Andrea Spadaccini | def run(self): |
71 | 91c10532 | Andrea Spadaccini | # Hypothesis: the messages we receive contain only a complete QMP message
|
72 | 91c10532 | Andrea Spadaccini | # encoded in JSON.
|
73 | 91c10532 | Andrea Spadaccini | conn, addr = self.socket.accept()
|
74 | 91c10532 | Andrea Spadaccini | |
75 | 91c10532 | Andrea Spadaccini | # Send the banner as the first thing
|
76 | 91c10532 | Andrea Spadaccini | conn.send(self.encode_string(self._QMP_BANNER_DATA)) |
77 | 91c10532 | Andrea Spadaccini | |
78 | 91c10532 | Andrea Spadaccini | # Expect qmp_capabilities and return an empty response
|
79 | 91c10532 | Andrea Spadaccini | conn.recv(4096)
|
80 | 91c10532 | Andrea Spadaccini | conn.send(self.encode_string(self._EMPTY_RESPONSE)) |
81 | 91c10532 | Andrea Spadaccini | |
82 | 91c10532 | Andrea Spadaccini | while True: |
83 | 91c10532 | Andrea Spadaccini | # We ignore the expected message, as the purpose of this object is not
|
84 | 91c10532 | Andrea Spadaccini | # to verify the correctness of the communication but to act as a
|
85 | 91c10532 | Andrea Spadaccini | # partner for the SUT (System Under Test, that is QmpConnection)
|
86 | 91c10532 | Andrea Spadaccini | msg = conn.recv(4096)
|
87 | 91c10532 | Andrea Spadaccini | if not msg: |
88 | 91c10532 | Andrea Spadaccini | break
|
89 | 91c10532 | Andrea Spadaccini | |
90 | 91c10532 | Andrea Spadaccini | if not self.script: |
91 | 91c10532 | Andrea Spadaccini | break
|
92 | 91c10532 | Andrea Spadaccini | response = self.script.pop(0) |
93 | 91c10532 | Andrea Spadaccini | if isinstance(response, str): |
94 | 91c10532 | Andrea Spadaccini | conn.send(response) |
95 | 91c10532 | Andrea Spadaccini | elif isinstance(response, list): |
96 | 91c10532 | Andrea Spadaccini | for chunk in response: |
97 | 91c10532 | Andrea Spadaccini | conn.send(chunk) |
98 | 91c10532 | Andrea Spadaccini | else:
|
99 | 91c10532 | Andrea Spadaccini | raise errors.ProgrammerError("Unknown response type for %s" % response) |
100 | 91c10532 | Andrea Spadaccini | |
101 | 91c10532 | Andrea Spadaccini | conn.close() |
102 | 91c10532 | Andrea Spadaccini | |
103 | 91c10532 | Andrea Spadaccini | def encode_string(self, message): |
104 | 91c10532 | Andrea Spadaccini | return (serializer.DumpJson(message, indent=False) + |
105 | 91c10532 | Andrea Spadaccini | hv_kvm.QmpConnection._MESSAGE_END_TOKEN) |
106 | 91c10532 | Andrea Spadaccini | |
107 | 91c10532 | Andrea Spadaccini | |
108 | 91c10532 | Andrea Spadaccini | class TestQmpMessage(testutils.GanetiTestCase): |
109 | 91c10532 | Andrea Spadaccini | def testSerialization(self): |
110 | 91c10532 | Andrea Spadaccini | test_data = {"execute": "command", "arguments": ["a", "b", "c"]} |
111 | 91c10532 | Andrea Spadaccini | message = hv_kvm.QmpMessage(test_data) |
112 | 91c10532 | Andrea Spadaccini | |
113 | 91c10532 | Andrea Spadaccini | for k, v in test_data.items(): |
114 | 91c10532 | Andrea Spadaccini | self.failUnless(message[k] == v)
|
115 | 91c10532 | Andrea Spadaccini | |
116 | 91c10532 | Andrea Spadaccini | rebuilt_message = hv_kvm.QmpMessage.BuildFromJsonString(str(message))
|
117 | 91c10532 | Andrea Spadaccini | self.failUnless(rebuilt_message == message)
|
118 | 91c10532 | Andrea Spadaccini | |
119 | 91c10532 | Andrea Spadaccini | |
120 | 91c10532 | Andrea Spadaccini | class TestQmp(testutils.GanetiTestCase): |
121 | 91c10532 | Andrea Spadaccini | def testQmp(self): |
122 | 91c10532 | Andrea Spadaccini | requests = [ |
123 | 91c10532 | Andrea Spadaccini | {"execute": "query-kvm", "arguments": []}, |
124 | 91c10532 | Andrea Spadaccini | {"execute": "eject", "arguments": {"device": "ide1-cd0"}}, |
125 | 91c10532 | Andrea Spadaccini | {"execute": "query-status", "arguments": []}, |
126 | 91c10532 | Andrea Spadaccini | {"execute": "query-name", "arguments": []}, |
127 | 91c10532 | Andrea Spadaccini | ] |
128 | 91c10532 | Andrea Spadaccini | |
129 | 91c10532 | Andrea Spadaccini | server_responses = [ |
130 | 91c10532 | Andrea Spadaccini | # One message, one send()
|
131 | 91c10532 | Andrea Spadaccini | '{"return": {"enabled": true, "present": true}}\r\n',
|
132 | 91c10532 | Andrea Spadaccini | |
133 | 91c10532 | Andrea Spadaccini | # Message sent using multiple send()
|
134 | 91c10532 | Andrea Spadaccini | ['{"retur', 'n": {}}\r\n'], |
135 | 91c10532 | Andrea Spadaccini | |
136 | 91c10532 | Andrea Spadaccini | # Multiple messages sent using one send()
|
137 | 91c10532 | Andrea Spadaccini | '{"return": [{"name": "quit"}, {"name": "eject"}]}\r\n'
|
138 | 91c10532 | Andrea Spadaccini | '{"return": {"running": true, "singlestep": false}}\r\n',
|
139 | 91c10532 | Andrea Spadaccini | ] |
140 | 91c10532 | Andrea Spadaccini | |
141 | 91c10532 | Andrea Spadaccini | expected_responses = [ |
142 | 91c10532 | Andrea Spadaccini | {"return": {"enabled": True, "present": True}}, |
143 | 91c10532 | Andrea Spadaccini | {"return": {}},
|
144 | 91c10532 | Andrea Spadaccini | {"return": [{"name": "quit"}, {"name": "eject"}]}, |
145 | 91c10532 | Andrea Spadaccini | {"return": {"running": True, "singlestep": False}}, |
146 | 91c10532 | Andrea Spadaccini | ] |
147 | 91c10532 | Andrea Spadaccini | |
148 | 91c10532 | Andrea Spadaccini | # Set up the stub
|
149 | 91c10532 | Andrea Spadaccini | socket_file = tempfile.NamedTemporaryFile() |
150 | 91c10532 | Andrea Spadaccini | os.remove(socket_file.name) |
151 | 91c10532 | Andrea Spadaccini | qmp_stub = QmpStub(socket_file.name, server_responses) |
152 | 91c10532 | Andrea Spadaccini | qmp_stub.start() |
153 | 91c10532 | Andrea Spadaccini | |
154 | 91c10532 | Andrea Spadaccini | # Set up the QMP connection
|
155 | 91c10532 | Andrea Spadaccini | qmp_connection = hv_kvm.QmpConnection(socket_file.name) |
156 | 91c10532 | Andrea Spadaccini | qmp_connection.connect() |
157 | 91c10532 | Andrea Spadaccini | |
158 | 91c10532 | Andrea Spadaccini | # Format the script
|
159 | 91c10532 | Andrea Spadaccini | for request, expected_response in zip(requests, expected_responses): |
160 | 91c10532 | Andrea Spadaccini | response = qmp_connection.Execute(request) |
161 | 91c10532 | Andrea Spadaccini | self.failUnless(response == hv_kvm.QmpMessage(expected_response))
|
162 | 91c10532 | Andrea Spadaccini | |
163 | 91c10532 | Andrea Spadaccini | |
164 | 55cc0a44 | Michael Hanselmann | class TestConsole(unittest.TestCase): |
165 | 55cc0a44 | Michael Hanselmann | def _Test(self, instance, hvparams): |
166 | 55cc0a44 | Michael Hanselmann | cons = hv_kvm.KVMHypervisor.GetInstanceConsole(instance, hvparams, {}) |
167 | 55cc0a44 | Michael Hanselmann | self.assertTrue(cons.Validate())
|
168 | 55cc0a44 | Michael Hanselmann | return cons
|
169 | 55cc0a44 | Michael Hanselmann | |
170 | 55cc0a44 | Michael Hanselmann | def testSerial(self): |
171 | 55cc0a44 | Michael Hanselmann | instance = objects.Instance(name="kvm.example.com",
|
172 | 55cc0a44 | Michael Hanselmann | primary_node="node6017")
|
173 | 55cc0a44 | Michael Hanselmann | hvparams = { |
174 | 55cc0a44 | Michael Hanselmann | constants.HV_SERIAL_CONSOLE: True,
|
175 | 55cc0a44 | Michael Hanselmann | constants.HV_VNC_BIND_ADDRESS: None,
|
176 | 4d2cdb5a | Andrea Spadaccini | constants.HV_KVM_SPICE_BIND: None,
|
177 | 55cc0a44 | Michael Hanselmann | } |
178 | 55cc0a44 | Michael Hanselmann | cons = self._Test(instance, hvparams)
|
179 | 55cc0a44 | Michael Hanselmann | self.assertEqual(cons.kind, constants.CONS_SSH)
|
180 | 55cc0a44 | Michael Hanselmann | self.assertEqual(cons.host, instance.primary_node)
|
181 | 2f4c951e | Stephen Shirley | self.assertEqual(cons.command[0], constants.KVM_CONSOLE_WRAPPER) |
182 | 2f4c951e | Stephen Shirley | self.assertEqual(cons.command[1], constants.SOCAT_PATH) |
183 | 55cc0a44 | Michael Hanselmann | |
184 | 55cc0a44 | Michael Hanselmann | def testVnc(self): |
185 | 55cc0a44 | Michael Hanselmann | instance = objects.Instance(name="kvm.example.com",
|
186 | 55cc0a44 | Michael Hanselmann | primary_node="node7235",
|
187 | 55cc0a44 | Michael Hanselmann | network_port=constants.VNC_BASE_PORT + 10)
|
188 | 55cc0a44 | Michael Hanselmann | hvparams = { |
189 | 55cc0a44 | Michael Hanselmann | constants.HV_SERIAL_CONSOLE: False,
|
190 | 55cc0a44 | Michael Hanselmann | constants.HV_VNC_BIND_ADDRESS: "192.0.2.1",
|
191 | 4d2cdb5a | Andrea Spadaccini | constants.HV_KVM_SPICE_BIND: None,
|
192 | 55cc0a44 | Michael Hanselmann | } |
193 | 55cc0a44 | Michael Hanselmann | cons = self._Test(instance, hvparams)
|
194 | 55cc0a44 | Michael Hanselmann | self.assertEqual(cons.kind, constants.CONS_VNC)
|
195 | 55cc0a44 | Michael Hanselmann | self.assertEqual(cons.host, "192.0.2.1") |
196 | 55cc0a44 | Michael Hanselmann | self.assertEqual(cons.port, constants.VNC_BASE_PORT + 10) |
197 | 55cc0a44 | Michael Hanselmann | self.assertEqual(cons.display, 10) |
198 | 55cc0a44 | Michael Hanselmann | |
199 | 4d2cdb5a | Andrea Spadaccini | def testSpice(self): |
200 | 4d2cdb5a | Andrea Spadaccini | instance = objects.Instance(name="kvm.example.com",
|
201 | 4d2cdb5a | Andrea Spadaccini | primary_node="node7235",
|
202 | 4d2cdb5a | Andrea Spadaccini | network_port=11000)
|
203 | 4d2cdb5a | Andrea Spadaccini | hvparams = { |
204 | 4d2cdb5a | Andrea Spadaccini | constants.HV_SERIAL_CONSOLE: False,
|
205 | 4d2cdb5a | Andrea Spadaccini | constants.HV_VNC_BIND_ADDRESS: None,
|
206 | 4d2cdb5a | Andrea Spadaccini | constants.HV_KVM_SPICE_BIND: "192.0.2.1",
|
207 | 4d2cdb5a | Andrea Spadaccini | } |
208 | 4d2cdb5a | Andrea Spadaccini | cons = self._Test(instance, hvparams)
|
209 | 4d2cdb5a | Andrea Spadaccini | self.assertEqual(cons.kind, constants.CONS_SPICE)
|
210 | 4d2cdb5a | Andrea Spadaccini | self.assertEqual(cons.host, "192.0.2.1") |
211 | 4d2cdb5a | Andrea Spadaccini | self.assertEqual(cons.port, 11000) |
212 | 4d2cdb5a | Andrea Spadaccini | |
213 | 55cc0a44 | Michael Hanselmann | def testNoConsole(self): |
214 | 55cc0a44 | Michael Hanselmann | instance = objects.Instance(name="kvm.example.com",
|
215 | 55cc0a44 | Michael Hanselmann | primary_node="node24325",
|
216 | 55cc0a44 | Michael Hanselmann | network_port=0)
|
217 | 55cc0a44 | Michael Hanselmann | hvparams = { |
218 | 55cc0a44 | Michael Hanselmann | constants.HV_SERIAL_CONSOLE: False,
|
219 | 55cc0a44 | Michael Hanselmann | constants.HV_VNC_BIND_ADDRESS: None,
|
220 | 4d2cdb5a | Andrea Spadaccini | constants.HV_KVM_SPICE_BIND: None,
|
221 | 55cc0a44 | Michael Hanselmann | } |
222 | 55cc0a44 | Michael Hanselmann | cons = self._Test(instance, hvparams)
|
223 | 55cc0a44 | Michael Hanselmann | self.assertEqual(cons.kind, constants.CONS_MESSAGE)
|
224 | 55cc0a44 | Michael Hanselmann | |
225 | 55cc0a44 | Michael Hanselmann | |
226 | 748e4b5a | Michael Hanselmann | if __name__ == "__main__": |
227 | 748e4b5a | Michael Hanselmann | testutils.GanetiTestProgram() |