Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.hypervisor.hv_kvm_unittest.py @ 1a2eb2dc

History | View | Annotate | Download (8.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 585c8187 Guido Trotter
from ganeti import utils
36 a56625a2 Michael Hanselmann
from ganeti import pathutils
37 748e4b5a Michael Hanselmann
38 748e4b5a Michael Hanselmann
from ganeti.hypervisor import hv_kvm
39 748e4b5a Michael Hanselmann
40 748e4b5a Michael Hanselmann
import testutils
41 748e4b5a Michael Hanselmann
42 748e4b5a Michael Hanselmann
43 91c10532 Andrea Spadaccini
class QmpStub(threading.Thread):
44 91c10532 Andrea Spadaccini
  """Stub for a QMP endpoint for a KVM instance
45 91c10532 Andrea Spadaccini

46 91c10532 Andrea Spadaccini
  """
47 51129a7f Michael Hanselmann
  _QMP_BANNER_DATA = {
48 51129a7f Michael Hanselmann
    "QMP": {
49 51129a7f Michael Hanselmann
      "version": {
50 51129a7f Michael Hanselmann
        "package": "",
51 51129a7f Michael Hanselmann
        "qemu": {
52 51129a7f Michael Hanselmann
          "micro": 50,
53 51129a7f Michael Hanselmann
          "minor": 13,
54 51129a7f Michael Hanselmann
          "major": 0,
55 51129a7f Michael Hanselmann
          },
56 51129a7f Michael Hanselmann
        "capabilities": [],
57 51129a7f Michael Hanselmann
        },
58 51129a7f Michael Hanselmann
      }
59 51129a7f Michael Hanselmann
    }
60 51129a7f Michael Hanselmann
  _EMPTY_RESPONSE = {
61 51129a7f Michael Hanselmann
    "return": [],
62 51129a7f Michael Hanselmann
    }
63 91c10532 Andrea Spadaccini
64 91c10532 Andrea Spadaccini
  def __init__(self, socket_filename, server_responses):
65 91c10532 Andrea Spadaccini
    """Creates a QMP stub
66 91c10532 Andrea Spadaccini

67 91c10532 Andrea Spadaccini
    @type socket_filename: string
68 91c10532 Andrea Spadaccini
    @param socket_filename: filename of the UNIX socket that will be created
69 91c10532 Andrea Spadaccini
                            this class and used for the communication
70 91c10532 Andrea Spadaccini
    @type server_responses: list
71 91c10532 Andrea Spadaccini
    @param server_responses: list of responses that the server sends in response
72 91c10532 Andrea Spadaccini
                             to whatever it receives
73 91c10532 Andrea Spadaccini
    """
74 91c10532 Andrea Spadaccini
    threading.Thread.__init__(self)
75 91c10532 Andrea Spadaccini
    self.socket_filename = socket_filename
76 91c10532 Andrea Spadaccini
    self.script = server_responses
77 91c10532 Andrea Spadaccini
78 91c10532 Andrea Spadaccini
    self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
79 91c10532 Andrea Spadaccini
    self.socket.bind(self.socket_filename)
80 91c10532 Andrea Spadaccini
    self.socket.listen(1)
81 91c10532 Andrea Spadaccini
82 91c10532 Andrea Spadaccini
  def run(self):
83 91c10532 Andrea Spadaccini
    # Hypothesis: the messages we receive contain only a complete QMP message
84 91c10532 Andrea Spadaccini
    # encoded in JSON.
85 91c10532 Andrea Spadaccini
    conn, addr = self.socket.accept()
86 91c10532 Andrea Spadaccini
87 91c10532 Andrea Spadaccini
    # Send the banner as the first thing
88 91c10532 Andrea Spadaccini
    conn.send(self.encode_string(self._QMP_BANNER_DATA))
89 91c10532 Andrea Spadaccini
90 91c10532 Andrea Spadaccini
    # Expect qmp_capabilities and return an empty response
91 91c10532 Andrea Spadaccini
    conn.recv(4096)
92 91c10532 Andrea Spadaccini
    conn.send(self.encode_string(self._EMPTY_RESPONSE))
93 91c10532 Andrea Spadaccini
94 91c10532 Andrea Spadaccini
    while True:
95 91c10532 Andrea Spadaccini
      # We ignore the expected message, as the purpose of this object is not
96 91c10532 Andrea Spadaccini
      # to verify the correctness of the communication but to act as a
97 91c10532 Andrea Spadaccini
      # partner for the SUT (System Under Test, that is QmpConnection)
98 91c10532 Andrea Spadaccini
      msg = conn.recv(4096)
99 91c10532 Andrea Spadaccini
      if not msg:
100 91c10532 Andrea Spadaccini
        break
101 91c10532 Andrea Spadaccini
102 91c10532 Andrea Spadaccini
      if not self.script:
103 91c10532 Andrea Spadaccini
        break
104 91c10532 Andrea Spadaccini
      response = self.script.pop(0)
105 91c10532 Andrea Spadaccini
      if isinstance(response, str):
106 91c10532 Andrea Spadaccini
        conn.send(response)
107 91c10532 Andrea Spadaccini
      elif isinstance(response, list):
108 91c10532 Andrea Spadaccini
        for chunk in response:
109 91c10532 Andrea Spadaccini
          conn.send(chunk)
110 91c10532 Andrea Spadaccini
      else:
111 91c10532 Andrea Spadaccini
        raise errors.ProgrammerError("Unknown response type for %s" % response)
112 91c10532 Andrea Spadaccini
113 91c10532 Andrea Spadaccini
    conn.close()
114 91c10532 Andrea Spadaccini
115 91c10532 Andrea Spadaccini
  def encode_string(self, message):
116 a182a3ed Michael Hanselmann
    return (serializer.DumpJson(message) +
117 91c10532 Andrea Spadaccini
            hv_kvm.QmpConnection._MESSAGE_END_TOKEN)
118 91c10532 Andrea Spadaccini
119 91c10532 Andrea Spadaccini
120 91c10532 Andrea Spadaccini
class TestQmpMessage(testutils.GanetiTestCase):
121 91c10532 Andrea Spadaccini
  def testSerialization(self):
122 51129a7f Michael Hanselmann
    test_data = {
123 51129a7f Michael Hanselmann
      "execute": "command",
124 51129a7f Michael Hanselmann
      "arguments": ["a", "b", "c"],
125 51129a7f Michael Hanselmann
      }
126 91c10532 Andrea Spadaccini
    message = hv_kvm.QmpMessage(test_data)
127 91c10532 Andrea Spadaccini
128 91c10532 Andrea Spadaccini
    for k, v in test_data.items():
129 51129a7f Michael Hanselmann
      self.assertEqual(message[k], v)
130 91c10532 Andrea Spadaccini
131 51129a7f Michael Hanselmann
    serialized = str(message)
132 51129a7f Michael Hanselmann
    self.assertEqual(len(serialized.splitlines()), 1,
133 51129a7f Michael Hanselmann
                     msg="Got multi-line message")
134 51129a7f Michael Hanselmann
135 51129a7f Michael Hanselmann
    rebuilt_message = hv_kvm.QmpMessage.BuildFromJsonString(serialized)
136 51129a7f Michael Hanselmann
    self.assertEqual(rebuilt_message, message)
137 91c10532 Andrea Spadaccini
138 91c10532 Andrea Spadaccini
139 91c10532 Andrea Spadaccini
class TestQmp(testutils.GanetiTestCase):
140 91c10532 Andrea Spadaccini
  def testQmp(self):
141 91c10532 Andrea Spadaccini
    requests = [
142 91c10532 Andrea Spadaccini
      {"execute": "query-kvm", "arguments": []},
143 91c10532 Andrea Spadaccini
      {"execute": "eject", "arguments": {"device": "ide1-cd0"}},
144 91c10532 Andrea Spadaccini
      {"execute": "query-status", "arguments": []},
145 91c10532 Andrea Spadaccini
      {"execute": "query-name", "arguments": []},
146 91c10532 Andrea Spadaccini
      ]
147 91c10532 Andrea Spadaccini
148 91c10532 Andrea Spadaccini
    server_responses = [
149 91c10532 Andrea Spadaccini
      # One message, one send()
150 91c10532 Andrea Spadaccini
      '{"return": {"enabled": true, "present": true}}\r\n',
151 91c10532 Andrea Spadaccini
152 91c10532 Andrea Spadaccini
      # Message sent using multiple send()
153 91c10532 Andrea Spadaccini
      ['{"retur', 'n": {}}\r\n'],
154 91c10532 Andrea Spadaccini
155 91c10532 Andrea Spadaccini
      # Multiple messages sent using one send()
156 91c10532 Andrea Spadaccini
      '{"return": [{"name": "quit"}, {"name": "eject"}]}\r\n'
157 91c10532 Andrea Spadaccini
      '{"return": {"running": true, "singlestep": false}}\r\n',
158 91c10532 Andrea Spadaccini
      ]
159 91c10532 Andrea Spadaccini
160 91c10532 Andrea Spadaccini
    expected_responses = [
161 91c10532 Andrea Spadaccini
      {"return": {"enabled": True, "present": True}},
162 91c10532 Andrea Spadaccini
      {"return": {}},
163 91c10532 Andrea Spadaccini
      {"return": [{"name": "quit"}, {"name": "eject"}]},
164 91c10532 Andrea Spadaccini
      {"return": {"running": True, "singlestep": False}},
165 91c10532 Andrea Spadaccini
      ]
166 91c10532 Andrea Spadaccini
167 91c10532 Andrea Spadaccini
    # Set up the stub
168 91c10532 Andrea Spadaccini
    socket_file = tempfile.NamedTemporaryFile()
169 91c10532 Andrea Spadaccini
    os.remove(socket_file.name)
170 91c10532 Andrea Spadaccini
    qmp_stub = QmpStub(socket_file.name, server_responses)
171 91c10532 Andrea Spadaccini
    qmp_stub.start()
172 91c10532 Andrea Spadaccini
173 91c10532 Andrea Spadaccini
    # Set up the QMP connection
174 91c10532 Andrea Spadaccini
    qmp_connection = hv_kvm.QmpConnection(socket_file.name)
175 91c10532 Andrea Spadaccini
    qmp_connection.connect()
176 91c10532 Andrea Spadaccini
177 91c10532 Andrea Spadaccini
    # Format the script
178 91c10532 Andrea Spadaccini
    for request, expected_response in zip(requests, expected_responses):
179 91c10532 Andrea Spadaccini
      response = qmp_connection.Execute(request)
180 51129a7f Michael Hanselmann
      msg = hv_kvm.QmpMessage(expected_response)
181 51129a7f Michael Hanselmann
      self.assertEqual(len(str(msg).splitlines()), 1,
182 51129a7f Michael Hanselmann
                       msg="Got multi-line message")
183 51129a7f Michael Hanselmann
      self.assertEqual(response, msg)
184 91c10532 Andrea Spadaccini
185 91c10532 Andrea Spadaccini
186 55cc0a44 Michael Hanselmann
class TestConsole(unittest.TestCase):
187 55cc0a44 Michael Hanselmann
  def _Test(self, instance, hvparams):
188 55cc0a44 Michael Hanselmann
    cons = hv_kvm.KVMHypervisor.GetInstanceConsole(instance, hvparams, {})
189 55cc0a44 Michael Hanselmann
    self.assertTrue(cons.Validate())
190 55cc0a44 Michael Hanselmann
    return cons
191 55cc0a44 Michael Hanselmann
192 55cc0a44 Michael Hanselmann
  def testSerial(self):
193 55cc0a44 Michael Hanselmann
    instance = objects.Instance(name="kvm.example.com",
194 55cc0a44 Michael Hanselmann
                                primary_node="node6017")
195 55cc0a44 Michael Hanselmann
    hvparams = {
196 55cc0a44 Michael Hanselmann
      constants.HV_SERIAL_CONSOLE: True,
197 55cc0a44 Michael Hanselmann
      constants.HV_VNC_BIND_ADDRESS: None,
198 4d2cdb5a Andrea Spadaccini
      constants.HV_KVM_SPICE_BIND: None,
199 55cc0a44 Michael Hanselmann
      }
200 55cc0a44 Michael Hanselmann
    cons = self._Test(instance, hvparams)
201 55cc0a44 Michael Hanselmann
    self.assertEqual(cons.kind, constants.CONS_SSH)
202 55cc0a44 Michael Hanselmann
    self.assertEqual(cons.host, instance.primary_node)
203 a56625a2 Michael Hanselmann
    self.assertEqual(cons.command[0], pathutils.KVM_CONSOLE_WRAPPER)
204 2f4c951e Stephen Shirley
    self.assertEqual(cons.command[1], constants.SOCAT_PATH)
205 55cc0a44 Michael Hanselmann
206 55cc0a44 Michael Hanselmann
  def testVnc(self):
207 55cc0a44 Michael Hanselmann
    instance = objects.Instance(name="kvm.example.com",
208 55cc0a44 Michael Hanselmann
                                primary_node="node7235",
209 55cc0a44 Michael Hanselmann
                                network_port=constants.VNC_BASE_PORT + 10)
210 55cc0a44 Michael Hanselmann
    hvparams = {
211 55cc0a44 Michael Hanselmann
      constants.HV_SERIAL_CONSOLE: False,
212 55cc0a44 Michael Hanselmann
      constants.HV_VNC_BIND_ADDRESS: "192.0.2.1",
213 4d2cdb5a Andrea Spadaccini
      constants.HV_KVM_SPICE_BIND: None,
214 55cc0a44 Michael Hanselmann
      }
215 55cc0a44 Michael Hanselmann
    cons = self._Test(instance, hvparams)
216 55cc0a44 Michael Hanselmann
    self.assertEqual(cons.kind, constants.CONS_VNC)
217 55cc0a44 Michael Hanselmann
    self.assertEqual(cons.host, "192.0.2.1")
218 55cc0a44 Michael Hanselmann
    self.assertEqual(cons.port, constants.VNC_BASE_PORT + 10)
219 55cc0a44 Michael Hanselmann
    self.assertEqual(cons.display, 10)
220 55cc0a44 Michael Hanselmann
221 4d2cdb5a Andrea Spadaccini
  def testSpice(self):
222 4d2cdb5a Andrea Spadaccini
    instance = objects.Instance(name="kvm.example.com",
223 4d2cdb5a Andrea Spadaccini
                                primary_node="node7235",
224 4d2cdb5a Andrea Spadaccini
                                network_port=11000)
225 4d2cdb5a Andrea Spadaccini
    hvparams = {
226 4d2cdb5a Andrea Spadaccini
      constants.HV_SERIAL_CONSOLE: False,
227 4d2cdb5a Andrea Spadaccini
      constants.HV_VNC_BIND_ADDRESS: None,
228 4d2cdb5a Andrea Spadaccini
      constants.HV_KVM_SPICE_BIND: "192.0.2.1",
229 4d2cdb5a Andrea Spadaccini
      }
230 4d2cdb5a Andrea Spadaccini
    cons = self._Test(instance, hvparams)
231 4d2cdb5a Andrea Spadaccini
    self.assertEqual(cons.kind, constants.CONS_SPICE)
232 4d2cdb5a Andrea Spadaccini
    self.assertEqual(cons.host, "192.0.2.1")
233 4d2cdb5a Andrea Spadaccini
    self.assertEqual(cons.port, 11000)
234 4d2cdb5a Andrea Spadaccini
235 55cc0a44 Michael Hanselmann
  def testNoConsole(self):
236 55cc0a44 Michael Hanselmann
    instance = objects.Instance(name="kvm.example.com",
237 55cc0a44 Michael Hanselmann
                                primary_node="node24325",
238 55cc0a44 Michael Hanselmann
                                network_port=0)
239 55cc0a44 Michael Hanselmann
    hvparams = {
240 55cc0a44 Michael Hanselmann
      constants.HV_SERIAL_CONSOLE: False,
241 55cc0a44 Michael Hanselmann
      constants.HV_VNC_BIND_ADDRESS: None,
242 4d2cdb5a Andrea Spadaccini
      constants.HV_KVM_SPICE_BIND: None,
243 55cc0a44 Michael Hanselmann
      }
244 55cc0a44 Michael Hanselmann
    cons = self._Test(instance, hvparams)
245 55cc0a44 Michael Hanselmann
    self.assertEqual(cons.kind, constants.CONS_MESSAGE)
246 55cc0a44 Michael Hanselmann
247 55cc0a44 Michael Hanselmann
248 585c8187 Guido Trotter
class TestVersionChecking(testutils.GanetiTestCase):
249 585c8187 Guido Trotter
  def testParseVersion(self):
250 585c8187 Guido Trotter
    parse = hv_kvm.KVMHypervisor._ParseKVMVersion
251 585c8187 Guido Trotter
    help_10 = utils.ReadFile(self._TestDataFilename("kvm_1.0_help.txt"))
252 585c8187 Guido Trotter
    help_01590 = utils.ReadFile(self._TestDataFilename("kvm_0.15.90_help.txt"))
253 585c8187 Guido Trotter
    help_0125 = utils.ReadFile(self._TestDataFilename("kvm_0.12.5_help.txt"))
254 585c8187 Guido Trotter
    help_091 = utils.ReadFile(self._TestDataFilename("kvm_0.9.1_help.txt"))
255 585c8187 Guido Trotter
    self.assertEqual(parse(help_10), ("1.0", 1, 0, 0))
256 585c8187 Guido Trotter
    self.assertEqual(parse(help_01590), ("0.15.90", 0, 15, 90))
257 585c8187 Guido Trotter
    self.assertEqual(parse(help_0125), ("0.12.5", 0, 12, 5))
258 585c8187 Guido Trotter
    self.assertEqual(parse(help_091), ("0.9.1", 0, 9, 1))
259 585c8187 Guido Trotter
260 585c8187 Guido Trotter
261 748e4b5a Michael Hanselmann
if __name__ == "__main__":
262 748e4b5a Michael Hanselmann
  testutils.GanetiTestProgram()