Statistics
| Branch: | Tag: | Revision:

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