Revision b255379d

b/Makefile.am
1038 1038
	test/data/vgreduce-removemissing-2.02.66-ok.txt \
1039 1039
	test/data/vgs-missing-pvs-2.02.02.txt \
1040 1040
	test/data/vgs-missing-pvs-2.02.66.txt \
1041
	test/data/xen-xm-list-4.0.1-dom0-only.txt \
1042
	test/data/xen-xm-list-4.0.1-four-instances.txt \
1041 1043
	test/py/ganeti-cli.test \
1042 1044
	test/py/gnt-cli.test \
1043 1045
	test/py/import-export_unittest-helper
b/lib/hypervisor/hv_xen.py
75 75
    return "cpus = [ %s ]" % ", ".join(map(_GetCPUMap, cpu_list))
76 76

  
77 77

  
78
def _RunXmList(fn, xmllist_errors):
79
  """Helper function for L{_GetXmList} to run "xm list".
80

  
81
  @type fn: callable
82
  @param fn: Function returning result of running C{xm list}
83
  @type xmllist_errors: list
84
  @param xmllist_errors: Error list
85
  @rtype: list
86

  
87
  """
88
  result = fn()
89
  if result.failed:
90
    logging.error("xm list failed (%s): %s", result.fail_reason,
91
                  result.output)
92
    xmllist_errors.append(result)
93
    raise utils.RetryAgain()
94

  
95
  # skip over the heading
96
  return result.stdout.splitlines()
97

  
98

  
99
def _ParseXmList(lines, include_node):
100
  """Parses the output of C{xm list}.
101

  
102
  @type lines: list
103
  @param lines: Output lines of C{xm list}
104
  @type include_node: boolean
105
  @param include_node: If True, return information for Dom0
106
  @return: list of tuple containing (name, id, memory, vcpus, state, time
107
    spent)
108

  
109
  """
110
  result = []
111

  
112
  # Iterate through all lines while ignoring header
113
  for line in lines[1:]:
114
    # The format of lines is:
115
    # Name      ID Mem(MiB) VCPUs State  Time(s)
116
    # Domain-0   0  3418     4 r-----    266.2
117
    data = line.split()
118
    if len(data) != 6:
119
      raise errors.HypervisorError("Can't parse output of xm list,"
120
                                   " line: %s" % line)
121
    try:
122
      data[1] = int(data[1])
123
      data[2] = int(data[2])
124
      data[3] = int(data[3])
125
      data[5] = float(data[5])
126
    except (TypeError, ValueError), err:
127
      raise errors.HypervisorError("Can't parse output of xm list,"
128
                                   " line: %s, error: %s" % (line, err))
129

  
130
    # skip the Domain-0 (optional)
131
    if include_node or data[0] != _DOM0_NAME:
132
      result.append(data)
133

  
134
  return result
135

  
136

  
137
def _GetXmList(fn, include_node, _timeout=5):
138
  """Return the list of running instances.
139

  
140
  See L{_RunXmList} and L{_ParseXmList} for parameter details.
141

  
142
  """
143
  xmllist_errors = []
144
  try:
145
    lines = utils.Retry(_RunXmList, (0.3, 1.5, 1.0), _timeout,
146
                        args=(fn, xmllist_errors))
147
  except utils.RetryTimeout:
148
    if xmllist_errors:
149
      xmlist_result = xmllist_errors.pop()
150

  
151
      errmsg = ("xm list failed, timeout exceeded (%s): %s" %
152
                (xmlist_result.fail_reason, xmlist_result.output))
153
    else:
154
      errmsg = "xm list failed"
155

  
156
    raise errors.HypervisorError(errmsg)
157

  
158
  return _ParseXmList(lines, include_node)
159

  
160

  
78 161
class XenHypervisor(hv_base.BaseHypervisor):
79 162
  """Xen generic hypervisor interface
80 163

  
......
154 237
    utils.RemoveFile(XenHypervisor._ConfigFileName(instance_name))
155 238

  
156 239
  @staticmethod
157
  def _RunXmList(xmlist_errors):
158
    """Helper function for L{_GetXMList} to run "xm list".
240
  def _GetXmList(include_node):
241
    """Wrapper around module level L{_GetXmList}.
159 242

  
160 243
    """
161
    result = utils.RunCmd([constants.XEN_CMD, "list"])
162
    if result.failed:
163
      logging.error("xm list failed (%s): %s", result.fail_reason,
164
                    result.output)
165
      xmlist_errors.append(result)
166
      raise utils.RetryAgain()
167

  
168
    # skip over the heading
169
    return result.stdout.splitlines()[1:]
170

  
171
  @classmethod
172
  def _GetXMList(cls, include_node):
173
    """Return the list of running instances.
174

  
175
    If the include_node argument is True, then we return information
176
    for dom0 also, otherwise we filter that from the return value.
177

  
178
    @return: list of (name, id, memory, vcpus, state, time spent)
179

  
180
    """
181
    xmlist_errors = []
182
    try:
183
      lines = utils.Retry(cls._RunXmList, 1, 5, args=(xmlist_errors, ))
184
    except utils.RetryTimeout:
185
      if xmlist_errors:
186
        xmlist_result = xmlist_errors.pop()
187

  
188
        errmsg = ("xm list failed, timeout exceeded (%s): %s" %
189
                  (xmlist_result.fail_reason, xmlist_result.output))
190
      else:
191
        errmsg = "xm list failed"
192

  
193
      raise errors.HypervisorError(errmsg)
194

  
195
    result = []
196
    for line in lines:
197
      # The format of lines is:
198
      # Name      ID Mem(MiB) VCPUs State  Time(s)
199
      # Domain-0   0  3418     4 r-----    266.2
200
      data = line.split()
201
      if len(data) != 6:
202
        raise errors.HypervisorError("Can't parse output of xm list,"
203
                                     " line: %s" % line)
204
      try:
205
        data[1] = int(data[1])
206
        data[2] = int(data[2])
207
        data[3] = int(data[3])
208
        data[5] = float(data[5])
209
      except (TypeError, ValueError), err:
210
        raise errors.HypervisorError("Can't parse output of xm list,"
211
                                     " line: %s, error: %s" % (line, err))
212

  
213
      # skip the Domain-0 (optional)
214
      if include_node or data[0] != _DOM0_NAME:
215
        result.append(data)
216

  
217
    return result
244
    # TODO: Abstract running Xen command for testing
245
    return _GetXmList(lambda: utils.RunCmd([constants.XEN_CMD, "list"]),
246
                      include_node)
218 247

  
219 248
  def ListInstances(self):
220 249
    """Get the list of running instances.
221 250

  
222 251
    """
223
    xm_list = self._GetXMList(False)
252
    xm_list = self._GetXmList(False)
224 253
    names = [info[0] for info in xm_list]
225 254
    return names
226 255

  
......
232 261
    @return: tuple (name, id, memory, vcpus, stat, times)
233 262

  
234 263
    """
235
    xm_list = self._GetXMList(instance_name == _DOM0_NAME)
264
    xm_list = self._GetXmList(instance_name == _DOM0_NAME)
236 265
    result = None
237 266
    for data in xm_list:
238 267
      if data[0] == instance_name:
......
246 275
    @return: list of tuples (name, id, memory, vcpus, stat, times)
247 276

  
248 277
    """
249
    xm_list = self._GetXMList(False)
278
    xm_list = self._GetXmList(False)
250 279
    return xm_list
251 280

  
252 281
  def StartInstance(self, instance, block_devices, startup_paused):
......
395 424
      result["cpu_sockets"] = nr_cpus / (cores_per_socket * threads_per_core)
396 425

  
397 426
    total_instmem = 0
398
    for (name, _, mem, vcpus, _, _) in self._GetXMList(True):
427
    for (name, _, mem, vcpus, _, _) in self._GetXmList(True):
399 428
      if name == _DOM0_NAME:
400 429
        result["memory_dom0"] = mem
401 430
        result["dom0_cpus"] = vcpus
b/test/data/xen-xm-list-4.0.1-dom0-only.txt
1
Name                                        ID   Mem VCPUs      State   Time(s)
2
Domain-0                                     0  1023     1     r----- 121152.6
b/test/data/xen-xm-list-4.0.1-four-instances.txt
1
Name                                        ID   Mem VCPUs      State   Time(s)
2
Domain-0                                     0  1023     1     r----- 154706.1
3
server01.example.com                         1  1024     1     -b---- 167643.2
4
web3106215069.example.com                    3  4096     1     -b---- 466690.9
5
testinstance.example.com                     2  2048     2     r----- 244443.0
b/test/py/ganeti.hypervisor.hv_xen_unittest.py
1 1
#!/usr/bin/python
2 2
#
3 3

  
4
# Copyright (C) 2011 Google Inc.
4
# Copyright (C) 2011, 2013 Google Inc.
5 5
#
6 6
# This program is free software; you can redistribute it and/or modify
7 7
# it under the terms of the GNU General Public License as published by
......
26 26
from ganeti import constants
27 27
from ganeti import objects
28 28
from ganeti import hypervisor
29
from ganeti import utils
30
from ganeti import errors
31
from ganeti import compat
29 32

  
30 33
from ganeti.hypervisor import hv_xen
31 34

  
......
63 66
                      constants.CPU_PINNING_ALL_XEN))
64 67

  
65 68

  
69
class TestParseXmList(testutils.GanetiTestCase):
70
  def test(self):
71
    data = testutils.ReadTestData("xen-xm-list-4.0.1-dom0-only.txt")
72

  
73
    # Exclude node
74
    self.assertEqual(hv_xen._ParseXmList(data.splitlines(), False), [])
75

  
76
    # Include node
77
    result = hv_xen._ParseXmList(data.splitlines(), True)
78
    self.assertEqual(len(result), 1)
79
    self.assertEqual(len(result[0]), 6)
80

  
81
    # Name
82
    self.assertEqual(result[0][0], hv_xen._DOM0_NAME)
83

  
84
    # ID
85
    self.assertEqual(result[0][1], 0)
86

  
87
    # Memory
88
    self.assertEqual(result[0][2], 1023)
89

  
90
    # VCPUs
91
    self.assertEqual(result[0][3], 1)
92

  
93
    # State
94
    self.assertEqual(result[0][4], "r-----")
95

  
96
    # Time
97
    self.assertAlmostEqual(result[0][5], 121152.6)
98

  
99
  def testWrongLineFormat(self):
100
    tests = [
101
      ["three fields only"],
102
      ["name InvalidID 128 1 r----- 12345"],
103
      ]
104

  
105
    for lines in tests:
106
      try:
107
        hv_xen._ParseXmList(["Header would be here"] + lines, False)
108
      except errors.HypervisorError, err:
109
        self.assertTrue("Can't parse output of xm list" in str(err))
110
      else:
111
        self.fail("Exception was not raised")
112

  
113

  
114
class TestGetXmList(testutils.GanetiTestCase):
115
  def _Fail(self):
116
    return utils.RunResult(constants.EXIT_FAILURE, None,
117
                           "stdout", "stderr", None,
118
                           NotImplemented, NotImplemented)
119

  
120
  def testTimeout(self):
121
    fn = testutils.CallCounter(self._Fail)
122
    try:
123
      hv_xen._GetXmList(fn, False, _timeout=0.1)
124
    except errors.HypervisorError, err:
125
      self.assertTrue("timeout exceeded" in str(err))
126
    else:
127
      self.fail("Exception was not raised")
128

  
129
    self.assertTrue(fn.Count() < 10,
130
                    msg="'xm list' was called too many times")
131

  
132
  def _Success(self, stdout):
133
    return utils.RunResult(constants.EXIT_SUCCESS, None, stdout, "", None,
134
                           NotImplemented, NotImplemented)
135

  
136
  def testSuccess(self):
137
    data = testutils.ReadTestData("xen-xm-list-4.0.1-four-instances.txt")
138

  
139
    fn = testutils.CallCounter(compat.partial(self._Success, data))
140

  
141
    result = hv_xen._GetXmList(fn, True, _timeout=0.1)
142

  
143
    self.assertEqual(len(result), 4)
144

  
145
    self.assertEqual(map(compat.fst, result), [
146
      "Domain-0",
147
      "server01.example.com",
148
      "web3106215069.example.com",
149
      "testinstance.example.com",
150
      ])
151

  
152
    self.assertEqual(fn.Count(), 1)
153

  
154

  
66 155
if __name__ == "__main__":
67 156
  testutils.GanetiTestProgram()

Also available in: Unified diff