Revision 06c9a520

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-info-4.0.1.txt \
1041 1042
	test/data/xen-xm-list-4.0.1-dom0-only.txt \
1042 1043
	test/data/xen-xm-list-4.0.1-four-instances.txt \
1043 1044
	test/py/ganeti-cli.test \
b/lib/hypervisor/hv_xen.py
158 158
  return _ParseXmList(lines, include_node)
159 159

  
160 160

  
161
def _ParseNodeInfo(info):
162
  """Return information about the node.
163

  
164
  @return: a dict with the following keys (memory values in MiB):
165
        - memory_total: the total memory size on the node
166
        - memory_free: the available memory on the node for instances
167
        - nr_cpus: total number of CPUs
168
        - nr_nodes: in a NUMA system, the number of domains
169
        - nr_sockets: the number of physical CPU sockets in the node
170
        - hv_version: the hypervisor version in the form (major, minor)
171

  
172
  """
173
  result = {}
174
  cores_per_socket = threads_per_core = nr_cpus = None
175
  xen_major, xen_minor = None, None
176
  memory_total = None
177
  memory_free = None
178

  
179
  for line in info.splitlines():
180
    fields = line.split(":", 1)
181

  
182
    if len(fields) < 2:
183
      continue
184

  
185
    (key, val) = map(lambda s: s.strip(), fields)
186

  
187
    # Note: in Xen 3, memory has changed to total_memory
188
    if key in ("memory", "total_memory"):
189
      memory_total = int(val)
190
    elif key == "free_memory":
191
      memory_free = int(val)
192
    elif key == "nr_cpus":
193
      nr_cpus = result["cpu_total"] = int(val)
194
    elif key == "nr_nodes":
195
      result["cpu_nodes"] = int(val)
196
    elif key == "cores_per_socket":
197
      cores_per_socket = int(val)
198
    elif key == "threads_per_core":
199
      threads_per_core = int(val)
200
    elif key == "xen_major":
201
      xen_major = int(val)
202
    elif key == "xen_minor":
203
      xen_minor = int(val)
204

  
205
  if None not in [cores_per_socket, threads_per_core, nr_cpus]:
206
    result["cpu_sockets"] = nr_cpus / (cores_per_socket * threads_per_core)
207

  
208
  if memory_free is not None:
209
    result["memory_free"] = memory_free
210

  
211
  if memory_total is not None:
212
    result["memory_total"] = memory_total
213

  
214
  if not (xen_major is None or xen_minor is None):
215
    result[constants.HV_NODEINFO_KEY_VERSION] = (xen_major, xen_minor)
216

  
217
  return result
218

  
219

  
220
def _MergeInstanceInfo(info, fn):
221
  """Updates node information from L{_ParseNodeInfo} with instance info.
222

  
223
  @type info: dict
224
  @param info: Result from L{_ParseNodeInfo}
225
  @type fn: callable
226
  @param fn: Function returning result of running C{xm list}
227
  @rtype: dict
228

  
229
  """
230
  total_instmem = 0
231

  
232
  for (name, _, mem, vcpus, _, _) in fn(True):
233
    if name == _DOM0_NAME:
234
      info["memory_dom0"] = mem
235
      info["dom0_cpus"] = vcpus
236

  
237
    # Include Dom0 in total memory usage
238
    total_instmem += mem
239

  
240
  memory_free = info.get("memory_free")
241
  memory_total = info.get("memory_total")
242

  
243
  # Calculate memory used by hypervisor
244
  if None not in [memory_total, memory_free, total_instmem]:
245
    info["memory_hv"] = memory_total - memory_free - total_instmem
246

  
247
  return info
248

  
249

  
250
def _GetNodeInfo(info, fn):
251
  """Combines L{_MergeInstanceInfo} and L{_ParseNodeInfo}.
252

  
253
  """
254
  return _MergeInstanceInfo(_ParseNodeInfo(info), fn)
255

  
256

  
161 257
class XenHypervisor(hv_base.BaseHypervisor):
162 258
  """Xen generic hypervisor interface
163 259

  
......
372 468
  def GetNodeInfo(self):
373 469
    """Return information about the node.
374 470

  
375
    @return: a dict with the following keys (memory values in MiB):
376
          - memory_total: the total memory size on the node
377
          - memory_free: the available memory on the node for instances
378
          - memory_dom0: the memory used by the node itself, if available
379
          - nr_cpus: total number of CPUs
380
          - nr_nodes: in a NUMA system, the number of domains
381
          - nr_sockets: the number of physical CPU sockets in the node
382
          - hv_version: the hypervisor version in the form (major, minor)
471
    @see: L{_GetNodeInfo} and L{_ParseNodeInfo}
383 472

  
384 473
    """
474
    # TODO: Abstract running Xen command for testing
385 475
    result = utils.RunCmd([constants.XEN_CMD, "info"])
386 476
    if result.failed:
387 477
      logging.error("Can't run 'xm info' (%s): %s", result.fail_reason,
388 478
                    result.output)
389 479
      return None
390 480

  
391
    xmoutput = result.stdout.splitlines()
392
    result = {}
393
    cores_per_socket = threads_per_core = nr_cpus = None
394
    xen_major, xen_minor = None, None
395
    memory_total = None
396
    memory_free = None
397

  
398
    for line in xmoutput:
399
      splitfields = line.split(":", 1)
400

  
401
      if len(splitfields) > 1:
402
        key = splitfields[0].strip()
403
        val = splitfields[1].strip()
404

  
405
        # note: in xen 3, memory has changed to total_memory
406
        if key == "memory" or key == "total_memory":
407
          memory_total = int(val)
408
        elif key == "free_memory":
409
          memory_free = int(val)
410
        elif key == "nr_cpus":
411
          nr_cpus = result["cpu_total"] = int(val)
412
        elif key == "nr_nodes":
413
          result["cpu_nodes"] = int(val)
414
        elif key == "cores_per_socket":
415
          cores_per_socket = int(val)
416
        elif key == "threads_per_core":
417
          threads_per_core = int(val)
418
        elif key == "xen_major":
419
          xen_major = int(val)
420
        elif key == "xen_minor":
421
          xen_minor = int(val)
422

  
423
    if None not in [cores_per_socket, threads_per_core, nr_cpus]:
424
      result["cpu_sockets"] = nr_cpus / (cores_per_socket * threads_per_core)
425

  
426
    total_instmem = 0
427
    for (name, _, mem, vcpus, _, _) in self._GetXmList(True):
428
      if name == _DOM0_NAME:
429
        result["memory_dom0"] = mem
430
        result["dom0_cpus"] = vcpus
431

  
432
      # Include Dom0 in total memory usage
433
      total_instmem += mem
434

  
435
    if memory_free is not None:
436
      result["memory_free"] = memory_free
437

  
438
    if memory_total is not None:
439
      result["memory_total"] = memory_total
440

  
441
    # Calculate memory used by hypervisor
442
    if None not in [memory_total, memory_free, total_instmem]:
443
      result["memory_hv"] = memory_total - memory_free - total_instmem
444

  
445
    if not (xen_major is None or xen_minor is None):
446
      result[constants.HV_NODEINFO_KEY_VERSION] = (xen_major, xen_minor)
447

  
448
    return result
481
    return _GetNodeInfo(result.stdout, self._GetXmList)
449 482

  
450 483
  @classmethod
451 484
  def GetInstanceConsole(cls, instance, hvparams, beparams):
b/test/data/xen-xm-info-4.0.1.txt
1
host                   : host.example.com
2
release                : 3.2.0
3
version                : #1 SMP Tue Jan  1 00:00:00 UTC 2013
4
machine                : x86_64
5
nr_cpus                : 4
6
nr_nodes               : 1
7
cores_per_socket       : 2
8
threads_per_core       : 1
9
cpu_mhz                : 2800
10
hw_caps                : bfebfbff:20100800:00000000:00000940:0004e3bd:00000000:00000001:00000000
11
virt_caps              : 
12
total_memory           : 16378
13
free_memory            : 8004
14
node_to_cpu            : node0:0-3
15
node_to_memory         : node0:8004
16
node_to_dma32_mem      : node0:2985
17
max_node_id            : 0
18
xen_major              : 4
19
xen_minor              : 0
20
xen_extra              : .1
21
xen_caps               : xen-3.0-x86_64 xen-3.0-x86_32p 
22
xen_scheduler          : credit
23
xen_pagesize           : 4096
24
platform_params        : virt_start=0xffff800000000000
25
xen_changeset          : unavailable
26
xen_commandline        : placeholder dom0_mem=1024M com1=115200,8n1 console=com1
27
cc_compiler            : gcc version 4.4.5 (Debian 4.4.5-8) 
28
cc_compile_by          : user
29
cc_compile_domain      : example.com
30
cc_compile_date        : Tue Jan  1 00:00:00 UTC 2013
31
xend_config_format     : 4
b/test/py/ganeti.hypervisor.hv_xen_unittest.py
152 152
    self.assertEqual(fn.Count(), 1)
153 153

  
154 154

  
155
class TestParseNodeInfo(testutils.GanetiTestCase):
156
  def testEmpty(self):
157
    self.assertEqual(hv_xen._ParseNodeInfo(""), {})
158

  
159
  def testUnknownInput(self):
160
    data = "\n".join([
161
      "foo bar",
162
      "something else goes",
163
      "here",
164
      ])
165
    self.assertEqual(hv_xen._ParseNodeInfo(data), {})
166

  
167
  def testBasicInfo(self):
168
    data = testutils.ReadTestData("xen-xm-info-4.0.1.txt")
169
    result = hv_xen._ParseNodeInfo(data)
170
    self.assertEqual(result, {
171
      "cpu_nodes": 1,
172
      "cpu_sockets": 2,
173
      "cpu_total": 4,
174
      "hv_version": (4, 0),
175
      "memory_free": 8004,
176
      "memory_total": 16378,
177
      })
178

  
179

  
180
class TestMergeInstanceInfo(testutils.GanetiTestCase):
181
  def testEmpty(self):
182
    self.assertEqual(hv_xen._MergeInstanceInfo({}, lambda _: []), {})
183

  
184
  def _FakeXmList(self, include_node):
185
    self.assertTrue(include_node)
186
    return [
187
      (hv_xen._DOM0_NAME, NotImplemented, 4096, 7, NotImplemented,
188
       NotImplemented),
189
      ("inst1.example.com", NotImplemented, 2048, 4, NotImplemented,
190
       NotImplemented),
191
      ]
192

  
193
  def testMissingNodeInfo(self):
194
    result = hv_xen._MergeInstanceInfo({}, self._FakeXmList)
195
    self.assertEqual(result, {
196
      "memory_dom0": 4096,
197
      "dom0_cpus": 7,
198
      })
199

  
200
  def testWithNodeInfo(self):
201
    info = testutils.ReadTestData("xen-xm-info-4.0.1.txt")
202
    result = hv_xen._GetNodeInfo(info, self._FakeXmList)
203
    self.assertEqual(result, {
204
      "cpu_nodes": 1,
205
      "cpu_sockets": 2,
206
      "cpu_total": 4,
207
      "dom0_cpus": 7,
208
      "hv_version": (4, 0),
209
      "memory_dom0": 4096,
210
      "memory_free": 8004,
211
      "memory_hv": 2230,
212
      "memory_total": 16378,
213
      })
214

  
215

  
155 216
if __name__ == "__main__":
156 217
  testutils.GanetiTestProgram()

Also available in: Unified diff