Statistics
| Branch: | Tag: | Revision:

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()