Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.hypervisor.hv_kvm_unittest.py @ 53d4cdf1

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

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

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