Revision 904d32bc

b/Makefile.am
425 425
	lib/hypervisor/hv_xen.py
426 426

  
427 427
hypervisor_hv_kvm_PYTHON = \
428
	lib/hypervisor/hv_kvm/__init__.py
428
	lib/hypervisor/hv_kvm/__init__.py \
429
	lib/hypervisor/hv_kvm/monitor.py
429 430

  
430 431
storage_PYTHON = \
431 432
	lib/storage/__init__.py \
b/lib/hypervisor/hv_kvm/__init__.py
69 69
from ganeti.hypervisor import hv_base
70 70
from ganeti.utils import wrapper as utils_wrapper
71 71

  
72
from ganeti.hypervisor.hv_kvm.monitor import QmpConnection, QmpMessage, \
73
                                             MonitorSocket
74

  
72 75

  
73 76
_KVM_NETWORK_SCRIPT = pathutils.CONF_DIR + "/kvm-vif-bridge"
74 77
_KVM_START_PAUSED_FLAG = "-S"
......
380 383
    return False
381 384

  
382 385

  
383
class QmpMessage:
384
  """QEMU Messaging Protocol (QMP) message.
385

  
386
  """
387
  def __init__(self, data):
388
    """Creates a new QMP message based on the passed data.
389

  
390
    """
391
    if not isinstance(data, dict):
392
      raise TypeError("QmpMessage must be initialized with a dict")
393

  
394
    self.data = data
395

  
396
  def __getitem__(self, field_name):
397
    """Get the value of the required field if present, or None.
398

  
399
    Overrides the [] operator to provide access to the message data,
400
    returning None if the required item is not in the message
401
    @return: the value of the field_name field, or None if field_name
402
             is not contained in the message
403

  
404
    """
405
    return self.data.get(field_name, None)
406

  
407
  def __setitem__(self, field_name, field_value):
408
    """Set the value of the required field_name to field_value.
409

  
410
    """
411
    self.data[field_name] = field_value
412

  
413
  def __len__(self):
414
    """Return the number of fields stored in this QmpMessage.
415

  
416
    """
417
    return len(self.data)
418

  
419
  def __delitem__(self, key):
420
    """Delete the specified element from the QmpMessage.
421

  
422
    """
423
    del(self.data[key])
424

  
425
  @staticmethod
426
  def BuildFromJsonString(json_string):
427
    """Build a QmpMessage from a JSON encoded string.
428

  
429
    @type json_string: str
430
    @param json_string: JSON string representing the message
431
    @rtype: L{QmpMessage}
432
    @return: a L{QmpMessage} built from json_string
433

  
434
    """
435
    # Parse the string
436
    data = serializer.LoadJson(json_string)
437
    return QmpMessage(data)
438

  
439
  def __str__(self):
440
    # The protocol expects the JSON object to be sent as a single line.
441
    return serializer.DumpJson(self.data)
442

  
443
  def __eq__(self, other):
444
    # When comparing two QmpMessages, we are interested in comparing
445
    # their internal representation of the message data
446
    return self.data == other.data
447

  
448

  
449
class MonitorSocket(object):
450
  _SOCKET_TIMEOUT = 5
451

  
452
  def __init__(self, monitor_filename):
453
    """Instantiates the MonitorSocket object.
454

  
455
    @type monitor_filename: string
456
    @param monitor_filename: the filename of the UNIX raw socket on which the
457
                             monitor (QMP or simple one) is listening
458

  
459
    """
460
    self.monitor_filename = monitor_filename
461
    self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
462
    # We want to fail if the server doesn't send a complete message
463
    # in a reasonable amount of time
464
    self.sock.settimeout(self._SOCKET_TIMEOUT)
465
    self._connected = False
466

  
467
  def _check_socket(self):
468
    sock_stat = None
469
    try:
470
      sock_stat = os.stat(self.monitor_filename)
471
    except EnvironmentError, err:
472
      if err.errno == errno.ENOENT:
473
        raise errors.HypervisorError("No monitor socket found")
474
      else:
475
        raise errors.HypervisorError("Error checking monitor socket: %s",
476
                                     utils.ErrnoOrStr(err))
477
    if not stat.S_ISSOCK(sock_stat.st_mode):
478
      raise errors.HypervisorError("Monitor socket is not a socket")
479

  
480
  def _check_connection(self):
481
    """Make sure that the connection is established.
482

  
483
    """
484
    if not self._connected:
485
      raise errors.ProgrammerError("To use a MonitorSocket you need to first"
486
                                   " invoke connect() on it")
487

  
488
  def connect(self):
489
    """Connects to the monitor.
490

  
491
    Connects to the UNIX socket
492

  
493
    @raise errors.HypervisorError: when there are communication errors
494

  
495
    """
496
    if self._connected:
497
      raise errors.ProgrammerError("Cannot connect twice")
498

  
499
    self._check_socket()
500

  
501
    # Check file existance/stuff
502
    try:
503
      self.sock.connect(self.monitor_filename)
504
    except EnvironmentError:
505
      raise errors.HypervisorError("Can't connect to qmp socket")
506
    self._connected = True
507

  
508
  def close(self):
509
    """Closes the socket
510

  
511
    It cannot be used after this call.
512

  
513
    """
514
    self.sock.close()
515

  
516

  
517
class QmpConnection(MonitorSocket):
518
  """Connection to the QEMU Monitor using the QEMU Monitor Protocol (QMP).
519

  
520
  """
521
  _FIRST_MESSAGE_KEY = "QMP"
522
  _EVENT_KEY = "event"
523
  _ERROR_KEY = "error"
524
  _RETURN_KEY = RETURN_KEY = "return"
525
  _ACTUAL_KEY = ACTUAL_KEY = "actual"
526
  _ERROR_CLASS_KEY = "class"
527
  _ERROR_DESC_KEY = "desc"
528
  _EXECUTE_KEY = "execute"
529
  _ARGUMENTS_KEY = "arguments"
530
  _CAPABILITIES_COMMAND = "qmp_capabilities"
531
  _MESSAGE_END_TOKEN = "\r\n"
532

  
533
  def __init__(self, monitor_filename):
534
    super(QmpConnection, self).__init__(monitor_filename)
535
    self._buf = ""
536

  
537
  def connect(self):
538
    """Connects to the QMP monitor.
539

  
540
    Connects to the UNIX socket and makes sure that we can actually send and
541
    receive data to the kvm instance via QMP.
542

  
543
    @raise errors.HypervisorError: when there are communication errors
544
    @raise errors.ProgrammerError: when there are data serialization errors
545

  
546
    """
547
    super(QmpConnection, self).connect()
548
    # Check if we receive a correct greeting message from the server
549
    # (As per the QEMU Protocol Specification 0.1 - section 2.2)
550
    greeting = self._Recv()
551
    if not greeting[self._FIRST_MESSAGE_KEY]:
552
      self._connected = False
553
      raise errors.HypervisorError("kvm: QMP communication error (wrong"
554
                                   " server greeting")
555

  
556
    # This is needed because QMP can return more than one greetings
557
    # see https://groups.google.com/d/msg/ganeti-devel/gZYcvHKDooU/SnukC8dgS5AJ
558
    self._buf = ""
559

  
560
    # Let's put the monitor in command mode using the qmp_capabilities
561
    # command, or else no command will be executable.
562
    # (As per the QEMU Protocol Specification 0.1 - section 4)
563
    self.Execute(self._CAPABILITIES_COMMAND)
564

  
565
  def _ParseMessage(self, buf):
566
    """Extract and parse a QMP message from the given buffer.
567

  
568
    Seeks for a QMP message in the given buf. If found, it parses it and
569
    returns it together with the rest of the characters in the buf.
570
    If no message is found, returns None and the whole buffer.
571

  
572
    @raise errors.ProgrammerError: when there are data serialization errors
573

  
574
    """
575
    message = None
576
    # Check if we got the message end token (CRLF, as per the QEMU Protocol
577
    # Specification 0.1 - Section 2.1.1)
578
    pos = buf.find(self._MESSAGE_END_TOKEN)
579
    if pos >= 0:
580
      try:
581
        message = QmpMessage.BuildFromJsonString(buf[:pos + 1])
582
      except Exception, err:
583
        raise errors.ProgrammerError("QMP data serialization error: %s" % err)
584
      buf = buf[pos + 1:]
585

  
586
    return (message, buf)
587

  
588
  def _Recv(self):
589
    """Receives a message from QMP and decodes the received JSON object.
590

  
591
    @rtype: QmpMessage
592
    @return: the received message
593
    @raise errors.HypervisorError: when there are communication errors
594
    @raise errors.ProgrammerError: when there are data serialization errors
595

  
596
    """
597
    self._check_connection()
598

  
599
    # Check if there is already a message in the buffer
600
    (message, self._buf) = self._ParseMessage(self._buf)
601
    if message:
602
      return message
603

  
604
    recv_buffer = StringIO.StringIO(self._buf)
605
    recv_buffer.seek(len(self._buf))
606
    try:
607
      while True:
608
        data = self.sock.recv(4096)
609
        if not data:
610
          break
611
        recv_buffer.write(data)
612

  
613
        (message, self._buf) = self._ParseMessage(recv_buffer.getvalue())
614
        if message:
615
          return message
616

  
617
    except socket.timeout, err:
618
      raise errors.HypervisorError("Timeout while receiving a QMP message: "
619
                                   "%s" % (err))
620
    except socket.error, err:
621
      raise errors.HypervisorError("Unable to receive data from KVM using the"
622
                                   " QMP protocol: %s" % err)
623

  
624
  def _Send(self, message):
625
    """Encodes and sends a message to KVM using QMP.
626

  
627
    @type message: QmpMessage
628
    @param message: message to send to KVM
629
    @raise errors.HypervisorError: when there are communication errors
630
    @raise errors.ProgrammerError: when there are data serialization errors
631

  
632
    """
633
    self._check_connection()
634
    try:
635
      message_str = str(message)
636
    except Exception, err:
637
      raise errors.ProgrammerError("QMP data deserialization error: %s" % err)
638

  
639
    try:
640
      self.sock.sendall(message_str)
641
    except socket.timeout, err:
642
      raise errors.HypervisorError("Timeout while sending a QMP message: "
643
                                   "%s (%s)" % (err.string, err.errno))
644
    except socket.error, err:
645
      raise errors.HypervisorError("Unable to send data from KVM using the"
646
                                   " QMP protocol: %s" % err)
647

  
648
  def Execute(self, command, arguments=None):
649
    """Executes a QMP command and returns the response of the server.
650

  
651
    @type command: str
652
    @param command: the command to execute
653
    @type arguments: dict
654
    @param arguments: dictionary of arguments to be passed to the command
655
    @rtype: dict
656
    @return: dictionary representing the received JSON object
657
    @raise errors.HypervisorError: when there are communication errors
658
    @raise errors.ProgrammerError: when there are data serialization errors
659

  
660
    """
661
    self._check_connection()
662
    message = QmpMessage({self._EXECUTE_KEY: command})
663
    if arguments:
664
      message[self._ARGUMENTS_KEY] = arguments
665
    self._Send(message)
666

  
667
    # Events can occur between the sending of the command and the reception
668
    # of the response, so we need to filter out messages with the event key.
669
    while True:
670
      response = self._Recv()
671
      err = response[self._ERROR_KEY]
672
      if err:
673
        raise errors.HypervisorError("kvm: error executing the %s"
674
                                     " command: %s (%s):" %
675
                                     (command,
676
                                      err[self._ERROR_DESC_KEY],
677
                                      err[self._ERROR_CLASS_KEY]))
678

  
679
      elif not response[self._EVENT_KEY]:
680
        return response
681

  
682

  
683 386
class KVMHypervisor(hv_base.BaseHypervisor):
684 387
  """KVM hypervisor interface
685 388

  
b/lib/hypervisor/hv_kvm/monitor.py
1
#
2
#
3

  
4
# Copyright (C) 2014 Google Inc.
5
# All rights reserved.
6
#
7
# Redistribution and use in source and binary forms, with or without
8
# modification, are permitted provided that the following conditions are
9
# met:
10
#
11
# 1. Redistributions of source code must retain the above copyright notice,
12
# this list of conditions and the following disclaimer.
13
#
14
# 2. Redistributions in binary form must reproduce the above copyright
15
# notice, this list of conditions and the following disclaimer in the
16
# documentation and/or other materials provided with the distribution.
17
#
18
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
19
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
22
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29

  
30

  
31
"""Qemu monitor control classes
32

  
33
"""
34

  
35
import os
36
import stat
37
import errno
38
import socket
39
import StringIO
40

  
41
from ganeti import errors
42
from ganeti import utils
43
from ganeti import serializer
44

  
45

  
46
class QmpMessage:
47
  """QEMU Messaging Protocol (QMP) message.
48

  
49
  """
50
  def __init__(self, data):
51
    """Creates a new QMP message based on the passed data.
52

  
53
    """
54
    if not isinstance(data, dict):
55
      raise TypeError("QmpMessage must be initialized with a dict")
56

  
57
    self.data = data
58

  
59
  def __getitem__(self, field_name):
60
    """Get the value of the required field if present, or None.
61

  
62
    Overrides the [] operator to provide access to the message data,
63
    returning None if the required item is not in the message
64
    @return: the value of the field_name field, or None if field_name
65
             is not contained in the message
66

  
67
    """
68
    return self.data.get(field_name, None)
69

  
70
  def __setitem__(self, field_name, field_value):
71
    """Set the value of the required field_name to field_value.
72

  
73
    """
74
    self.data[field_name] = field_value
75

  
76
  def __len__(self):
77
    """Return the number of fields stored in this QmpMessage.
78

  
79
    """
80
    return len(self.data)
81

  
82
  def __delitem__(self, key):
83
    """Delete the specified element from the QmpMessage.
84

  
85
    """
86
    del(self.data[key])
87

  
88
  @staticmethod
89
  def BuildFromJsonString(json_string):
90
    """Build a QmpMessage from a JSON encoded string.
91

  
92
    @type json_string: str
93
    @param json_string: JSON string representing the message
94
    @rtype: L{QmpMessage}
95
    @return: a L{QmpMessage} built from json_string
96

  
97
    """
98
    # Parse the string
99
    data = serializer.LoadJson(json_string)
100
    return QmpMessage(data)
101

  
102
  def __str__(self):
103
    # The protocol expects the JSON object to be sent as a single line.
104
    return serializer.DumpJson(self.data)
105

  
106
  def __eq__(self, other):
107
    # When comparing two QmpMessages, we are interested in comparing
108
    # their internal representation of the message data
109
    return self.data == other.data
110

  
111

  
112
class MonitorSocket(object):
113
  _SOCKET_TIMEOUT = 5
114

  
115
  def __init__(self, monitor_filename):
116
    """Instantiates the MonitorSocket object.
117

  
118
    @type monitor_filename: string
119
    @param monitor_filename: the filename of the UNIX raw socket on which the
120
                             monitor (QMP or simple one) is listening
121

  
122
    """
123
    self.monitor_filename = monitor_filename
124
    self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
125
    # We want to fail if the server doesn't send a complete message
126
    # in a reasonable amount of time
127
    self.sock.settimeout(self._SOCKET_TIMEOUT)
128
    self._connected = False
129

  
130
  def _check_socket(self):
131
    sock_stat = None
132
    try:
133
      sock_stat = os.stat(self.monitor_filename)
134
    except EnvironmentError, err:
135
      if err.errno == errno.ENOENT:
136
        raise errors.HypervisorError("No monitor socket found")
137
      else:
138
        raise errors.HypervisorError("Error checking monitor socket: %s",
139
                                     utils.ErrnoOrStr(err))
140
    if not stat.S_ISSOCK(sock_stat.st_mode):
141
      raise errors.HypervisorError("Monitor socket is not a socket")
142

  
143
  def _check_connection(self):
144
    """Make sure that the connection is established.
145

  
146
    """
147
    if not self._connected:
148
      raise errors.ProgrammerError("To use a MonitorSocket you need to first"
149
                                   " invoke connect() on it")
150

  
151
  def connect(self):
152
    """Connects to the monitor.
153

  
154
    Connects to the UNIX socket
155

  
156
    @raise errors.HypervisorError: when there are communication errors
157

  
158
    """
159
    if self._connected:
160
      raise errors.ProgrammerError("Cannot connect twice")
161

  
162
    self._check_socket()
163

  
164
    # Check file existance/stuff
165
    try:
166
      self.sock.connect(self.monitor_filename)
167
    except EnvironmentError:
168
      raise errors.HypervisorError("Can't connect to qmp socket")
169
    self._connected = True
170

  
171
  def close(self):
172
    """Closes the socket
173

  
174
    It cannot be used after this call.
175

  
176
    """
177
    self.sock.close()
178

  
179

  
180
class QmpConnection(MonitorSocket):
181
  """Connection to the QEMU Monitor using the QEMU Monitor Protocol (QMP).
182

  
183
  """
184
  _FIRST_MESSAGE_KEY = "QMP"
185
  _EVENT_KEY = "event"
186
  _ERROR_KEY = "error"
187
  _RETURN_KEY = RETURN_KEY = "return"
188
  _ACTUAL_KEY = ACTUAL_KEY = "actual"
189
  _ERROR_CLASS_KEY = "class"
190
  _ERROR_DESC_KEY = "desc"
191
  _EXECUTE_KEY = "execute"
192
  _ARGUMENTS_KEY = "arguments"
193
  _CAPABILITIES_COMMAND = "qmp_capabilities"
194
  _MESSAGE_END_TOKEN = "\r\n"
195

  
196
  def __init__(self, monitor_filename):
197
    super(QmpConnection, self).__init__(monitor_filename)
198
    self._buf = ""
199

  
200
  def connect(self):
201
    """Connects to the QMP monitor.
202

  
203
    Connects to the UNIX socket and makes sure that we can actually send and
204
    receive data to the kvm instance via QMP.
205

  
206
    @raise errors.HypervisorError: when there are communication errors
207
    @raise errors.ProgrammerError: when there are data serialization errors
208

  
209
    """
210
    super(QmpConnection, self).connect()
211
    # Check if we receive a correct greeting message from the server
212
    # (As per the QEMU Protocol Specification 0.1 - section 2.2)
213
    greeting = self._Recv()
214
    if not greeting[self._FIRST_MESSAGE_KEY]:
215
      self._connected = False
216
      raise errors.HypervisorError("kvm: QMP communication error (wrong"
217
                                   " server greeting")
218

  
219
    # This is needed because QMP can return more than one greetings
220
    # see https://groups.google.com/d/msg/ganeti-devel/gZYcvHKDooU/SnukC8dgS5AJ
221
    self._buf = ""
222

  
223
    # Let's put the monitor in command mode using the qmp_capabilities
224
    # command, or else no command will be executable.
225
    # (As per the QEMU Protocol Specification 0.1 - section 4)
226
    self.Execute(self._CAPABILITIES_COMMAND)
227

  
228
  def _ParseMessage(self, buf):
229
    """Extract and parse a QMP message from the given buffer.
230

  
231
    Seeks for a QMP message in the given buf. If found, it parses it and
232
    returns it together with the rest of the characters in the buf.
233
    If no message is found, returns None and the whole buffer.
234

  
235
    @raise errors.ProgrammerError: when there are data serialization errors
236

  
237
    """
238
    message = None
239
    # Check if we got the message end token (CRLF, as per the QEMU Protocol
240
    # Specification 0.1 - Section 2.1.1)
241
    pos = buf.find(self._MESSAGE_END_TOKEN)
242
    if pos >= 0:
243
      try:
244
        message = QmpMessage.BuildFromJsonString(buf[:pos + 1])
245
      except Exception, err:
246
        raise errors.ProgrammerError("QMP data serialization error: %s" % err)
247
      buf = buf[pos + 1:]
248

  
249
    return (message, buf)
250

  
251
  def _Recv(self):
252
    """Receives a message from QMP and decodes the received JSON object.
253

  
254
    @rtype: QmpMessage
255
    @return: the received message
256
    @raise errors.HypervisorError: when there are communication errors
257
    @raise errors.ProgrammerError: when there are data serialization errors
258

  
259
    """
260
    self._check_connection()
261

  
262
    # Check if there is already a message in the buffer
263
    (message, self._buf) = self._ParseMessage(self._buf)
264
    if message:
265
      return message
266

  
267
    recv_buffer = StringIO.StringIO(self._buf)
268
    recv_buffer.seek(len(self._buf))
269
    try:
270
      while True:
271
        data = self.sock.recv(4096)
272
        if not data:
273
          break
274
        recv_buffer.write(data)
275

  
276
        (message, self._buf) = self._ParseMessage(recv_buffer.getvalue())
277
        if message:
278
          return message
279

  
280
    except socket.timeout, err:
281
      raise errors.HypervisorError("Timeout while receiving a QMP message: "
282
                                   "%s" % (err))
283
    except socket.error, err:
284
      raise errors.HypervisorError("Unable to receive data from KVM using the"
285
                                   " QMP protocol: %s" % err)
286

  
287
  def _Send(self, message):
288
    """Encodes and sends a message to KVM using QMP.
289

  
290
    @type message: QmpMessage
291
    @param message: message to send to KVM
292
    @raise errors.HypervisorError: when there are communication errors
293
    @raise errors.ProgrammerError: when there are data serialization errors
294

  
295
    """
296
    self._check_connection()
297
    try:
298
      message_str = str(message)
299
    except Exception, err:
300
      raise errors.ProgrammerError("QMP data deserialization error: %s" % err)
301

  
302
    try:
303
      self.sock.sendall(message_str)
304
    except socket.timeout, err:
305
      raise errors.HypervisorError("Timeout while sending a QMP message: "
306
                                   "%s (%s)" % (err.string, err.errno))
307
    except socket.error, err:
308
      raise errors.HypervisorError("Unable to send data from KVM using the"
309
                                   " QMP protocol: %s" % err)
310

  
311
  def Execute(self, command, arguments=None):
312
    """Executes a QMP command and returns the response of the server.
313

  
314
    @type command: str
315
    @param command: the command to execute
316
    @type arguments: dict
317
    @param arguments: dictionary of arguments to be passed to the command
318
    @rtype: dict
319
    @return: dictionary representing the received JSON object
320
    @raise errors.HypervisorError: when there are communication errors
321
    @raise errors.ProgrammerError: when there are data serialization errors
322

  
323
    """
324
    self._check_connection()
325
    message = QmpMessage({self._EXECUTE_KEY: command})
326
    if arguments:
327
      message[self._ARGUMENTS_KEY] = arguments
328
    self._Send(message)
329

  
330
    # Events can occur between the sending of the command and the reception
331
    # of the response, so we need to filter out messages with the event key.
332
    while True:
333
      response = self._Recv()
334
      err = response[self._ERROR_KEY]
335
      if err:
336
        raise errors.HypervisorError("kvm: error executing the %s"
337
                                     " command: %s (%s):" %
338
                                     (command,
339
                                      err[self._ERROR_DESC_KEY],
340
                                      err[self._ERROR_CLASS_KEY]))
341

  
342
      elif not response[self._EVENT_KEY]:
343
        return response

Also available in: Unified diff