Merge ~newell-jensen/maas:lxd-commissioning-hooks-memory into maas:master

Proposed by Newell Jensen
Status: Merged
Approved by: Newell Jensen
Approved revision: 0e10985bdf6ece6075af36e6695bec2e32855a37
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~newell-jensen/maas:lxd-commissioning-hooks-memory
Merge into: maas:master
Diff against target: 468 lines (+245/-98)
2 files modified
src/metadataserver/builtin_scripts/hooks.py (+20/-35)
src/metadataserver/builtin_scripts/tests/test_hooks.py (+225/-63)
Reviewer Review Type Date Requested Status
Blake Rouse (community) Approve
Lee Trager (community) Needs Fixing
MAAS Lander Needs Fixing
Review via email: mp+372503@code.launchpad.net

Commit message

Use LXD commissioning memory output instead of lshw memory output when post processing the script result hooks. Update cpu_speed to iterate over the sockets to take average frequency.

To post a comment you must log in.
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b lxd-commissioning-hooks-memory lp:~newell-jensen/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: FAILED
LOG: http://maas-ci-jenkins.internal:8080/job/maas/job/branch-tester/6371/console
COMMIT: c9db21b36a34b48c50c200870ffa914d6e5a51fb

review: Needs Fixing
Revision history for this message
Lee Trager (ltrager) wrote :

When the CPU frequency isn't found in the CPU model name its value should be the maximum value, not the average.

review: Needs Fixing
24c6cac... by Newell Jensen

Review updates.

0e10985... by Newell Jensen

Update current average frequency when there is no max.

Revision history for this message
Blake Rouse (blake-rouse) wrote :

Looks good!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/metadataserver/builtin_scripts/hooks.py b/src/metadataserver/builtin_scripts/hooks.py
2index 75928e2..e488e44 100644
3--- a/src/metadataserver/builtin_scripts/hooks.py
4+++ b/src/metadataserver/builtin_scripts/hooks.py
5@@ -7,13 +7,11 @@ __all__ = [
6 'NODE_INFO_SCRIPTS',
7 'parse_lshw_nic_info',
8 'update_node_network_information',
9- 'lxd_update_cpu_details',
10 ]
11
12 import fnmatch
13 import json
14 import logging
15-import math
16 import re
17
18 from lxml import etree
19@@ -298,8 +296,8 @@ def get_xml_field_value(evaluator, expression):
20 def update_hardware_details(node, output, exit_status):
21 """Process the results of `LSHW_SCRIPT`.
22
23- Updates `node.memory`, and `node.storage` fields, and also
24- evaluates all tag expressions against the given ``lshw`` XML.
25+ Updates `node.storage` fields, and also evaluates all tag
26+ expressions against the given ``lshw`` XML.
27
28 If `exit_status` is non-zero, this function returns without doing
29 anything.
30@@ -318,20 +316,6 @@ def update_hardware_details(node, output, exit_status):
31 # Same document, many queries: use XPathEvaluator.
32 evaluator = etree.XPathEvaluator(doc)
33
34- # Some machines have a <size> element in their memory <node> with the
35- # total amount of memory, and other machines declare the size of the
36- # memory in individual memory banks. This expression is mean to cope
37- # with both.
38- memory = evaluator("""\
39- sum(//node[@id='memory']/size[@units='bytes'] |
40- //node[starts-with(@id, 'memory:')]
41- /node[starts-with(@id, 'bank:')]/size[@units='bytes'])
42- div 1024 div 1024
43- """)
44- if not memory or math.isnan(memory):
45- memory = 0
46- node.memory = memory
47-
48 # Only one hardware UUID should be provided but lxml always returns a
49 # list.
50 for e in evaluator('//node/configuration/setting[@id="uuid"]'):
51@@ -339,7 +323,7 @@ def update_hardware_details(node, output, exit_status):
52 if value:
53 node.hardware_uuid = value
54
55- node.save(update_fields=['memory', 'hardware_uuid'])
56+ node.save(update_fields=['hardware_uuid'])
57
58 # This gathers the system vendor, product, version, and serial. Custom
59 # built machines and some Supermicro servers do not provide this
60@@ -389,15 +373,9 @@ def process_lxd_results(node, output, exit_status):
61 raise ValueError(e.message + ': ' + output)
62
63 # Update CPU details.
64- lxd_update_cpu_details(node, data)
65-
66-
67-def lxd_update_cpu_details(node, data):
68- """Updates `node.cpu_count`, `node.cpu_speed`, `node.cpu_model`"""
69- # cpu_count, cpu_speed, cpu_model.
70 node.cpu_count, node.cpu_speed, cpu_model = _parse_lxd_cpuinfo(data)
71- # memory.
72- node.memory = data.get('memory', {}).get('total', 0)
73+ # Update memory.
74+ node.memory = data.get('memory', {}).get('total', 0) / 1024 / 1024
75
76 if cpu_model:
77 NodeMetadata.objects.update_or_create(
78@@ -435,19 +413,26 @@ def _parse_lxd_cpuinfo(data):
79 if m is not None:
80 cpu_speed = int(float(m.group('ghz')) * 1000)
81 # When socket names don't match or cpu_speed couldn't be retrieved,
82- # use the max frequency if available before resulting to current
83- # frequency average.
84+ # use the max frequency among all the sockets if before
85+ # resulting to average current frequency of all the sockets.
86 if not cpu_speed:
87- frequency_turbo = socket.get('frequency_turbo')
88- if frequency_turbo is not None:
89- cpu_speed = frequency_turbo
90+ max_frequency = 0
91+ for socket in sockets:
92+ frequency_turbo = socket.get('frequency_turbo', 0)
93+ if frequency_turbo > max_frequency:
94+ max_frequency = frequency_turbo
95+ if max_frequency:
96+ cpu_speed = max_frequency
97 else:
98- frequency_current_average = socket.get('frequency')
99- if frequency_current_average is not None:
100+ current_average = 0
101+ for socket in sockets:
102+ current_average += socket.get('frequency', 0)
103+ current_average /= len(sockets)
104+ if current_average:
105 # Fall back on the current speed, round it to
106 # the nearest hundredth as the number may be
107 # effected by CPU scaling.
108- cpu_speed = round(frequency_current_average / 100) * 100
109+ cpu_speed = round(current_average / 100) * 100
110
111 return cpu_count, cpu_speed, cpu_model
112
113diff --git a/src/metadataserver/builtin_scripts/tests/test_hooks.py b/src/metadataserver/builtin_scripts/tests/test_hooks.py
114index bb68e1a..870c351 100644
115--- a/src/metadataserver/builtin_scripts/tests/test_hooks.py
116+++ b/src/metadataserver/builtin_scripts/tests/test_hooks.py
117@@ -41,7 +41,7 @@ from metadataserver.builtin_scripts.hooks import (
118 extract_router_mac_addresses,
119 filter_modaliases,
120 get_dmi_data,
121- lxd_update_cpu_details,
122+ process_lxd_results,
123 retag_node_for_hardware_by_modalias,
124 set_virtual_tag,
125 SWITCH_OPENBMC_MAC,
126@@ -88,7 +88,7 @@ lldp_output_interface_template = """
127 """
128
129
130-SAMPLE_JSON_CPUINFO = {
131+SAMPLE_LXD_JSON = {
132 "cpu": {
133 "architecture": "x86_64",
134 "sockets": [
135@@ -135,14 +135,212 @@ SAMPLE_JSON_CPUINFO = {
136 }
137 ],
138 "frequency": 3247
139+ },
140+ {
141+ "core": 1,
142+ "numa_node": 0,
143+ "threads": [
144+ {
145+ "id": 2,
146+ "thread": 0,
147+ "online": True
148+ },
149+ {
150+ "id": 3,
151+ "thread": 1,
152+ "online": True
153+ }
154+ ],
155+ "frequency": 3192
156+ },
157+ {
158+ "core": 2,
159+ "numa_node": 0,
160+ "threads": [
161+ {
162+ "id": 4,
163+ "thread": 0,
164+ "online": True
165+ },
166+ {
167+ "id": 5,
168+ "thread": 1,
169+ "online": True
170+ }
171+ ],
172+ "frequency": 3241
173+ },
174+ {
175+ "core": 3,
176+ "numa_node": 0,
177+ "threads": [
178+ {
179+ "id": 6,
180+ "thread": 0,
181+ "online": True
182+ },
183+ {
184+ "id": 7,
185+ "thread": 1,
186+ "online": True
187+ }
188+ ],
189+ "frequency": 3247
190 }
191 ],
192- "frequency": 3247,
193+ "frequency": 3231,
194 "frequency_minimum": 800,
195 "frequency_turbo": 3400
196 }
197 ],
198 "total": 8
199+ },
200+ "memory": {
201+ "nodes": [
202+ {
203+ "numa_node": 0,
204+ "hugepages_used": 0,
205+ "hugepages_total": 0,
206+ "used": 16430092288,
207+ "total": 16691519488
208+ }
209+ ],
210+ "hugepages_total": 0,
211+ "hugepages_used": 0,
212+ "hugepages_size": 2097152,
213+ "used": 12116492288,
214+ "total": 16691519488
215+ },
216+ "gpu": {
217+ "cards": [
218+ {
219+ "driver": "i915",
220+ "driver_version": "4.15.0-58-generic",
221+ "drm": {
222+ "id": 0,
223+ "card_name": "card0",
224+ "card_device": "226:0",
225+ "control_name": "controlD64",
226+ "control_device": "226:0",
227+ "render_name": "renderD128",
228+ "render_device": "226:128"
229+ },
230+ "numa_node": 0,
231+ "pci_address": "0000:00:02.0",
232+ "vendor": "Intel Corporation",
233+ "vendor_id": "8086",
234+ "product": (
235+ "4th Gen Core Processor Integrated Graphics Controller"),
236+ "product_id": "0416"
237+ }
238+ ],
239+ "total": 1
240+ },
241+ "network": {
242+ "cards": [
243+ {
244+ "driver": "e1000e",
245+ "driver_version": "3.2.6-k",
246+ "ports": [
247+ {
248+ "id": "enp0s25",
249+ "address": "54:ee:75:13:aa:52",
250+ "port": 0,
251+ "protocol": "ethernet",
252+ "supported_modes": [
253+ "10baseT/Half",
254+ "10baseT/Full",
255+ "100baseT/Half",
256+ "100baseT/Full",
257+ "1000baseT/Full"
258+ ],
259+ "supported_ports": [
260+ "twisted pair"
261+ ],
262+ "port_type": "twisted pair",
263+ "transceiver_type": "internal",
264+ "auto_negotiation": True,
265+ "link_detected": False
266+ }
267+ ],
268+ "numa_node": 0,
269+ "pci_address": "0000:00:19.0",
270+ "vendor": "Intel Corporation",
271+ "vendor_id": "8086",
272+ "product": "Ethernet Connection I217-LM",
273+ "product_id": "153a"
274+ },
275+ {
276+ "driver": "iwlwifi",
277+ "driver_version": "4.15.0-58-generic",
278+ "ports": [
279+ {
280+ "id": "wlp4s0",
281+ "address": "e8:2a:ea:22:0b:d2",
282+ "port": 0,
283+ "protocol": "ethernet",
284+ "auto_negotiation": False,
285+ "link_detected": True
286+ }
287+ ],
288+ "numa_node": 0,
289+ "pci_address": "0000:04:00.0",
290+ "vendor": "Intel Corporation",
291+ "vendor_id": "8086",
292+ "product": "Wireless 7260",
293+ "product_id": "08b2"
294+ }
295+ ],
296+ "total": 2
297+ },
298+ "storage": {
299+ "disks": [
300+ {
301+ "id": "sda",
302+ "device": "8:0",
303+ "model": "Crucial_CT512M55",
304+ "type": "scsi",
305+ "read_only": False,
306+ "size": 512110190592,
307+ "removable": False,
308+ "numa_node": 0,
309+ "partitions": [
310+ {
311+ "id": "sda1",
312+ "device": "8:1",
313+ "read_only": False,
314+ "size": 536870912,
315+ "partition": 1
316+ },
317+ {
318+ "id": "sda2",
319+ "device": "8:2",
320+ "read_only": False,
321+ "size": 511705088,
322+ "partition": 2
323+ },
324+ {
325+ "id": "sda3",
326+ "device": "8:3",
327+ "read_only": False,
328+ "size": 511060213760,
329+ "partition": 3
330+ }
331+ ]
332+ },
333+ {
334+ "id": "sr0",
335+ "device": "11:0",
336+ "model": "DVD-RAM UJ8E2",
337+ "type": "scsi",
338+ "read_only": False,
339+ "size": 1073741312,
340+ "removable": True,
341+ "numa_node": 0,
342+ "partitions": []
343+ }
344+ ],
345+ "total": 5
346 }
347 }
348
349@@ -726,43 +924,6 @@ class TestUpdateHardwareDetails(MAASServerTestCase):
350
351 doctest_flags = doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE
352
353- def test_hardware_updates_memory(self):
354- node = factory.make_Node()
355- xmlbytes = dedent("""\
356- <node id="memory">
357- <size units="bytes">4294967296</size>
358- </node>
359- """).encode("utf-8")
360- update_hardware_details(node, xmlbytes, 0)
361- node = reload_object(node)
362- self.assertEqual(4096, node.memory)
363-
364- def test_hardware_updates_memory_lenovo(self):
365- node = factory.make_Node()
366- xmlbytes = dedent("""\
367- <node>
368- <node id="memory:0" class="memory">
369- <node id="bank:0" class="memory" handle="DMI:002D">
370- <size units="bytes">4294967296</size>
371- </node>
372- <node id="bank:1" class="memory" handle="DMI:002E">
373- <size units="bytes">3221225472</size>
374- </node>
375- </node>
376- <node id="memory:1" class="memory">
377- <node id="bank:0" class="memory" handle="DMI:002F">
378- <size units="bytes">536870912</size>
379- </node>
380- </node>
381- <node id="memory:2" class="memory"></node>
382- </node>
383- """).encode("utf-8")
384- update_hardware_details(node, xmlbytes, 0)
385- node = reload_object(node)
386- mega = 2 ** 20
387- expected = (4294967296 + 3221225472 + 536879812) // mega
388- self.assertEqual(expected, node.memory)
389-
390 def test_hardware_updates_hardware_uuid(self):
391 node = factory.make_Node()
392 hardware_uuid = factory.make_UUID()
393@@ -882,53 +1043,54 @@ class TestUpdateHardwareDetails(MAASServerTestCase):
394 self.assertIsNone(NodeMetadata.objects.get(node=node, key=key))
395
396
397-class TestLXDUpdateCPUDetails(MAASServerTestCase):
398+class TestProcessLXDResults(MAASServerTestCase):
399
400- doctest_flags = doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE
401+ def test__updates_memory(self):
402+ node = factory.make_Node()
403+ node.memory = random.randint(4096, 8192)
404+ node.save()
405+
406+ process_lxd_results(
407+ node, json.dumps(SAMPLE_LXD_JSON).encode('utf-8'), 0)
408+ node = reload_object(node)
409+ self.assertEqual(round(16691519488 / 1024 / 1024), node.memory)
410
411- def test__speed_in_name(self):
412+ def test__updates_model_and_cpu_speed_from_name(self):
413 node = factory.make_Node()
414- node.cpu_count = 2
415 node.cpu_speed = 9999
416 node.save()
417
418- lxd_update_cpu_details(node, SAMPLE_JSON_CPUINFO)
419+ process_lxd_results(
420+ node, json.dumps(SAMPLE_LXD_JSON).encode('utf-8'), 0)
421 node = reload_object(node)
422- self.assertEqual(8, node.cpu_count)
423 self.assertEqual(2400, node.cpu_speed)
424 nmd = NodeMetadata.objects.get(node=node, key='cpu_model')
425 self.assertEqual('Intel(R) Core(TM) i7-4700MQ CPU', nmd.value)
426
427- def test__max_speed(self):
428+ def test__updates_cpu_speed_with_max_frequency_when_not_in_name(self):
429 node = factory.make_Node()
430- node.cpu_count = 2
431 node.cpu_speed = 9999
432 node.save()
433
434- SAMPLE_JSON_CPUINFO_NO_SPEED_IN_NAME = deepcopy(SAMPLE_JSON_CPUINFO)
435- SAMPLE_JSON_CPUINFO_NO_SPEED_IN_NAME['cpu']['sockets'][0]['name'] = (
436+ NO_SPEED_IN_NAME = deepcopy(SAMPLE_LXD_JSON)
437+ NO_SPEED_IN_NAME['cpu']['sockets'][0]['name'] = (
438 'Intel(R) Core(TM) i7-4700MQ CPU')
439- lxd_update_cpu_details(node, SAMPLE_JSON_CPUINFO_NO_SPEED_IN_NAME)
440+ process_lxd_results(
441+ node, json.dumps(NO_SPEED_IN_NAME).encode('utf-8'), 0)
442 node = reload_object(node)
443- self.assertEqual(8, node.cpu_count)
444 self.assertEqual(3400, node.cpu_speed)
445- nmd = NodeMetadata.objects.get(node=node, key='cpu_model')
446- self.assertEqual('Intel(R) Core(TM) i7-4700MQ CPU', nmd.value)
447
448- def test__current_speed(self):
449+ def test__updates_cpu_speed_with_current_frequency_when_not_in_name(self):
450 node = factory.make_Node()
451- node.cpu_count = 2
452 node.cpu_speed = 9999
453 node.save()
454
455- SAMPLE_JSON_CPUINFO_NO_NAME_OR_MAX_FREQ = deepcopy(SAMPLE_JSON_CPUINFO)
456- del SAMPLE_JSON_CPUINFO_NO_NAME_OR_MAX_FREQ[
457- 'cpu']['sockets'][0]['name']
458- del SAMPLE_JSON_CPUINFO_NO_NAME_OR_MAX_FREQ[
459- 'cpu']['sockets'][0]['frequency_turbo']
460- lxd_update_cpu_details(node, SAMPLE_JSON_CPUINFO_NO_NAME_OR_MAX_FREQ)
461+ NO_NAME_OR_MAX_FREQ = deepcopy(SAMPLE_LXD_JSON)
462+ del NO_NAME_OR_MAX_FREQ['cpu']['sockets'][0]['name']
463+ del NO_NAME_OR_MAX_FREQ['cpu']['sockets'][0]['frequency_turbo']
464+ process_lxd_results(
465+ node, json.dumps(NO_NAME_OR_MAX_FREQ).encode('utf-8'), 0)
466 node = reload_object(node)
467- self.assertEqual(8, node.cpu_count)
468 self.assertEqual(3200, node.cpu_speed)
469
470

Subscribers

People subscribed via source and target branches