Merge ~cberner/maas/+git/maas:lan_channel into maas:master

Proposed by Christopher Berner
Status: Rejected
Rejected by: Blake Rouse
Proposed branch: ~cberner/maas/+git/maas:lan_channel
Merge into: maas:master
Diff against target: 695 lines (+217/-127)
2 files modified
src/metadataserver/user_data/templates/snippets/maas_ipmi_autodetect.py (+130/-89)
src/metadataserver/user_data/templates/snippets/tests/test_maas_ipmi_autodetect.py (+87/-38)
Reviewer Review Type Date Requested Status
Newell Jensen (community) Needs Information
MAAS Lander Approve
Andres Rodriguez Pending
Review via email: mp+336024@code.launchpad.net

Description of the change

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

UNIT TESTS
-b lan_channel lp:~cberner/maas into -b master lp:~maas-committers/maas

STATUS: FAILED
LOG: http://maas-ci-jenkins.internal:8080/job/maas/job/branch-tester/1231/console
COMMIT: 0388bec1e989356397778c757173d5e7798d2763

review: Needs Fixing
Revision history for this message
Newell Jensen (newell-jensen) wrote :

Chris,

Thanks for this merge proposal. We unit test virtually everything in MAAS so you will need to update the unit test for this file as well, test_ipmi_maas_autodetect.py. To run the unit tests for this portion of the code you can issue the command from the branch root directory as such:

bin/test.region metadataserver.user_data.templates.snippets.tests.test_maas_ipmi_autodetect

You will need to run make before this to generate the testing infrastructure.

See HACKING.txt as well for more details on this.

Let me know if you have any further questions. Additionally, Andres was the one that claimed this review so he may have more comments to add as well.

Cheers.

review: Needs Fixing
Revision history for this message
Mike Pontillo (mpontillo) wrote :

To add to what Newell said, if you run 'make install-dependencies' and then 'make', the requirements to run the unit tests should be pulled in. Thanks for your contribution!

~cberner/maas/+git/maas:lan_channel updated
f2fd477... by Christopher Berner

Fix tests

ca7718f... by Christopher Berner

Add test for multiple LAN channels

Revision history for this message
Christopher Berner (cberner) wrote :

Thanks for the pointers! Think I got the tests fixed, and added one to check the multiple lan channel functionality. Let me know if there are other changes needed.

Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b lan_channel lp:~cberner/maas/+git/maas into -b master lp:~maas-committers/maas

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

review: Needs Fixing
Revision history for this message
Christopher Berner (cberner) wrote :

Hmm, are there more that need fixing? Tried running all the tests with "make test", but many fail for me even on the upstream master branch

Revision history for this message
Newell Jensen (newell-jensen) wrote :

> Hmm, are there more that need fixing? Tried running all the tests with "make
> test", but many fail for me even on the upstream master branch

There are some randomly failing tests that we have been trying to address in master so this may be what you are seeing.

Revision history for this message
Newell Jensen (newell-jensen) wrote :

Something I just noticed when taking a closer look at the manual page for bmc-config (and ipmi-config for that matter) command is that it doesn't have a "--lan-channel" parameter but instead has a "--lan-channel-number" parameter. Am I missing something here?

Additionally, might be a better idea to pick a random lan channel number instead of having '1' hardcoded everywhere in your unit tests.

e.g.

lan_channel = random.randint(1, 8)

review: Needs Information
~cberner/maas/+git/maas:lan_channel updated
bd5ce06... by Christopher Berner

Fix linter errors

LP: #1742299

affd2d7... by Christopher Berner

Use documented --lan-channel-number flag

Seems that --lan-channel works, but is undocumented

08281c3... by Christopher Berner

Change lan channel tests to use more randomization

Revision history for this message
Christopher Berner (cberner) wrote :

Thanks for the feedback, Newell, and sorry about the slow turn around here!

I updated this PR to address your comments, let me know if there's anything else you'd like to see changed.

Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b lan_channel lp:~cberner/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: bd5ce06535ef54f990922e7629268867e42a305e

review: Approve
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b lan_channel lp:~cberner/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: 08281c34e3ff0746f6bca50ad5ac33a731b876a8

review: Approve
Revision history for this message
Newell Jensen (newell-jensen) wrote :

See comment/suggestion inline.

review: Needs Information
Revision history for this message
Christopher Berner (cberner) wrote :

Replied inline. The current behavior seemed correct to me, but let me know if my assumption is wrong and I'll definitely change it

Revision history for this message
Newell Jensen (newell-jensen) wrote :

Andres, this branch has been dangling for some time and we should do something about it. You mentioned that you wanted to review it as well. Will ping you directly about this as well.

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

Rejecting due to inactivity.

Unmerged commits

08281c3... by Christopher Berner

Change lan channel tests to use more randomization

affd2d7... by Christopher Berner

Use documented --lan-channel-number flag

Seems that --lan-channel works, but is undocumented

bd5ce06... by Christopher Berner

Fix linter errors

LP: #1742299

ca7718f... by Christopher Berner

Add test for multiple LAN channels

f2fd477... by Christopher Berner

Fix tests

0388bec... by Christopher Berner

Add LAN channel auto detection

This allows IPMI detection to work even when the LAN channel used is
not the default channel

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/metadataserver/user_data/templates/snippets/maas_ipmi_autodetect.py b/src/metadataserver/user_data/templates/snippets/maas_ipmi_autodetect.py
2index e2c3ce5..268e77a 100644
3--- a/src/metadataserver/user_data/templates/snippets/maas_ipmi_autodetect.py
4+++ b/src/metadataserver/user_data/templates/snippets/maas_ipmi_autodetect.py
5@@ -40,16 +40,22 @@ def run_command(command_args):
6 return output.decode('utf-8')
7
8
9-def bmc_get(key):
10+def bmc_get(key, lan_channel):
11 """Fetch the output of a key via bmc-config checkout."""
12- command = ('bmc-config', '--checkout', '--key-pair=%s' % key)
13+ command = ('bmc-config',
14+ '--lan-channel-number=%d' % lan_channel,
15+ '--checkout',
16+ '--key-pair=%s' % key)
17 output = run_command(command)
18 return output
19
20
21-def bmc_set(key, value):
22+def bmc_set(key, value, lan_channel):
23 """Set the value of a key via bmc-config commit."""
24- command = ('bmc-config', '--commit', '--key-pair=%s=%s' % (key, value))
25+ command = ('bmc-config',
26+ '--lan-channel-number=%d' % lan_channel,
27+ '--commit',
28+ '--key-pair=%s=%s' % (key, value))
29 run_command(command)
30
31
32@@ -58,10 +64,10 @@ def format_user_key(user_number, parameter):
33 return '%s:%s' % (user_number, parameter)
34
35
36-def bmc_user_get(user_number, parameter):
37+def bmc_user_get(user_number, parameter, lan_channel):
38 """Get a user parameter via bmc-config commit."""
39 key = format_user_key(user_number, parameter)
40- raw = bmc_get(key)
41+ raw = bmc_get(key, lan_channel)
42 pattern = r'^\s*%s(?:[ \t])+([^#\s]+[^\n]*)$' % (re.escape(parameter))
43 match = re.search(pattern, raw, re.MULTILINE)
44 if match is None:
45@@ -69,29 +75,29 @@ def bmc_user_get(user_number, parameter):
46 return match.group(1)
47
48
49-def bmc_user_set(user_number, parameter, value):
50+def bmc_user_set(user_number, parameter, value, lan_channel):
51 """Set a user parameter via bmc-config commit."""
52 key = format_user_key(user_number, parameter)
53- bmc_set(key, value)
54+ bmc_set(key, value, lan_channel)
55
56
57-def bmc_list_sections():
58+def bmc_list_sections(lan_channel):
59 """Retrieve the names of config sections from the BMC."""
60- command = ('bmc-config', '-L')
61+ command = ('bmc-config', '-L', '--lan-channel-number=%d' % lan_channel)
62 output = run_command(command)
63 return output
64
65
66-def list_user_numbers():
67+def list_user_numbers(lan_channel):
68 """List the user numbers on the BMC."""
69- output = bmc_list_sections()
70+ output = bmc_list_sections(lan_channel)
71 pattern = r'^(User\d+)$'
72 users = re.findall(pattern, output, re.MULTILINE)
73
74 return users
75
76
77-def pick_user_number_from_list(search_username, user_numbers):
78+def pick_user_number_from_list(search_username, user_numbers, lan_channel):
79 """Pick the best user number for a user from a list of user numbers.
80
81 If any any existing user's username matches the search username, pick
82@@ -108,7 +114,7 @@ def pick_user_number_from_list(search_username, user_numbers):
83 if user_number == 'User1':
84 continue
85
86- username = bmc_user_get(user_number, 'Username')
87+ username = bmc_user_get(user_number, 'Username', lan_channel)
88
89 if username == search_username:
90 return user_number
91@@ -121,10 +127,11 @@ def pick_user_number_from_list(search_username, user_numbers):
92 return first_unused
93
94
95-def pick_user_number(search_username):
96+def pick_user_number(search_username, lan_channel):
97 """Pick the best user number for a username."""
98- user_numbers = list_user_numbers()
99- user_number = pick_user_number_from_list(search_username, user_numbers)
100+ user_numbers = list_user_numbers(lan_channel)
101+ user_number = pick_user_number_from_list(
102+ search_username, user_numbers, lan_channel)
103
104 if not user_number:
105 raise IPMIError('No IPMI user slots available.')
106@@ -132,49 +139,72 @@ def pick_user_number(search_username):
107 return user_number
108
109
110-def is_ipmi_dhcp():
111- output = bmc_get('Lan_Conf:IP_Address_Source')
112+def get_lan_channels():
113+ lan_channels = []
114+ show_re = re.compile('IP_Address_Source')
115+ for channel in range(1, 12):
116+ try:
117+ output = bmc_get('Lan_Conf:IP_Address_Source', channel)
118+ # Not all channels are LAN channels
119+ except subprocess.CalledProcessError:
120+ continue
121+ if show_re.search(output) is not None:
122+ lan_channels.append(channel)
123+ return lan_channels
124+
125+
126+def is_ipmi_dhcp(lan_channels):
127+ found_dhcp = False
128 show_re = re.compile('IP_Address_Source\s+Use_DHCP')
129- return show_re.search(output) is not None
130+ for channel in lan_channels:
131+ output = bmc_get('Lan_Conf:IP_Address_Source', channel)
132+ if show_re.search(output) is None:
133+ return False
134+ else:
135+ found_dhcp = True
136+ return found_dhcp
137
138
139-def set_ipmi_network_source(source):
140- bmc_set('Lan_Conf:IP_Address_Source', source)
141+def set_ipmi_network_source(source, lan_channels):
142+ for channel in lan_channels:
143+ bmc_set('Lan_Conf:IP_Address_Source', source, channel)
144
145
146-def _bmc_get_ipmi_addresses(address_type):
147+def _bmc_get_ipmi_addresses(address_type, lan_channel):
148 try:
149- return bmc_get(address_type)
150+ return bmc_get(address_type, lan_channel)
151 except subprocess.CalledProcessError:
152 return ""
153
154
155-def get_ipmi_ip_address():
156+def get_ipmi_ip_address(lan_channels):
157 show_re = re.compile(
158 '((?:[0-9]{1,3}\.){3}[0-9]{1,3}|[0-9a-fA-F]*:[0-9a-fA-F:.]+)')
159- for address_type in [
160- 'Lan_Conf:IP_Address',
161- 'Lan6_Conf:IPv6_Static_Addresses',
162- 'Lan6_Conf:IPv6_Dynamic_Addresses']:
163- output = _bmc_get_ipmi_addresses(address_type)
164- # Loop through the addreses by preference: IPv4, static IPv6, dynamic
165- # IPv6. Return the first valid, non-link-local address we find.
166- # While we could conceivably allow link-local addresses, we would need
167- # to devine which of our interfaces is the correct link, and then we
168- # would need support for link-local addresses in freeipmi-tools.
169- res = show_re.findall(output)
170- for ip in res:
171- if ip.lower().startswith('fe80::') or ip == '0.0.0.0':
172- time.sleep(2)
173- continue
174- if address_type.startswith('Lan6_'):
175- return '[%s]' % ip
176- return ip
177+ for channel in lan_channels:
178+ for address_type in [
179+ 'Lan_Conf:IP_Address',
180+ 'Lan6_Conf:IPv6_Static_Addresses',
181+ 'Lan6_Conf:IPv6_Dynamic_Addresses']:
182+ output = _bmc_get_ipmi_addresses(address_type, channel)
183+ # Loop through the addreses by preference: IPv4, static IPv6,
184+ # dynamic IPv6. Return the first valid, non-link-local address we
185+ # find. While we could conceivably allow link-local addresses, we
186+ # would need to devine which of our interfaces is the correct link,
187+ # and then we # would need support for link-local addresses in
188+ # freeipmi-tools.
189+ res = show_re.findall(output)
190+ for ip in res:
191+ if ip.lower().startswith('fe80::') or ip == '0.0.0.0':
192+ time.sleep(2)
193+ continue
194+ if address_type.startswith('Lan6_'):
195+ return '[%s]' % ip
196+ return ip
197 # No valid IP address was found.
198 return None
199
200
201-def verify_ipmi_user_settings(user_number, user_settings):
202+def verify_ipmi_user_settings(user_number, user_settings, lan_channel):
203 """Verify user settings were applied correctly."""
204
205 bad_values = {}
206@@ -183,7 +213,7 @@ def verify_ipmi_user_settings(user_number, user_settings):
207 # Password isn't included in checkout. Plus,
208 # some older BMCs may not support Enable_User.
209 if key not in ['Enable_User', 'Password']:
210- value = bmc_user_get(user_number, key)
211+ value = bmc_user_get(user_number, key, lan_channel)
212 if value != expected_value:
213 bad_values[key] = value
214
215@@ -199,15 +229,16 @@ def verify_ipmi_user_settings(user_number, user_settings):
216 raise IPMIError(message)
217
218
219-def apply_ipmi_user_settings(user_settings):
220+def apply_ipmi_user_settings(user_settings, lan_channels):
221 """Commit and verify IPMI user settings."""
222- username = user_settings['Username']
223- ipmi_user_number = pick_user_number(username)
224+ for channel in lan_channels:
225+ username = user_settings['Username']
226+ ipmi_user_number = pick_user_number(username, channel)
227
228- for key, value in user_settings.items():
229- bmc_user_set(ipmi_user_number, key, value)
230+ for key, value in user_settings.items():
231+ bmc_user_set(ipmi_user_number, key, value, channel)
232
233- verify_ipmi_user_settings(ipmi_user_number, user_settings)
234+ verify_ipmi_user_settings(ipmi_user_number, user_settings, channel)
235
236
237 def make_ipmi_user_settings(username, password):
238@@ -229,38 +260,45 @@ def make_ipmi_user_settings(username, password):
239 return user_settings
240
241
242-def configure_ipmi_user(username):
243+def configure_ipmi_user(username, lan_channels):
244 """Create or configure an IPMI user for remote use."""
245 for password in [generate_random_password(),
246 generate_random_password(with_special_chars=True)]:
247 user_settings = make_ipmi_user_settings(username, password)
248 try:
249- apply_ipmi_user_settings(user_settings)
250+ apply_ipmi_user_settings(user_settings, lan_channels)
251 return password
252 except subprocess.CalledProcessError:
253 pass
254 raise IPMIError("Unable to set BMC password.")
255
256
257-def set_ipmi_lan_channel_settings():
258+def set_ipmi_lan_channel_settings(lan_channels):
259 """Enable IPMI-over-Lan (Lan_Channel) if it is disabled"""
260 for mode in ['Lan_Channel:Volatile_Access_Mode',
261 'Lan_Channel:Non_Volatile_Access_Mode']:
262- output = bmc_get(mode)
263- show_re = re.compile('%s\s+Always_Available' % mode.split(':')[1])
264- if show_re.search(output) is None:
265- # Some BMC's don't support setting Lan_Channel (see LP: #1287274).
266- # If that happens, it would cause the script to fail preventing
267- # the script from continuing. To address this, simply catch the
268- # error, return and allow the script to continue.
269- try:
270- bmc_set(mode, 'Always_Available')
271- except subprocess.CalledProcessError:
272- return
273-
274-
275-def commit_ipmi_settings(config):
276- run_command(('bmc-config', '--commit', '--filename', config))
277+ for channel in lan_channels:
278+ output = bmc_get(mode, channel)
279+ show_re = re.compile('%s\s+Always_Available' % mode.split(':')[1])
280+ if show_re.search(output) is None:
281+ # Some BMC's don't support setting Lan_Channel
282+ # (see LP: #1287274).
283+ # If that happens, it would cause the script to fail preventing
284+ # the script from continuing. To address this, simply catch the
285+ # error, return and allow the script to continue.
286+ try:
287+ bmc_set(mode, 'Always_Available', channel)
288+ except subprocess.CalledProcessError:
289+ return
290+
291+
292+def commit_ipmi_settings(config, lan_channels):
293+ for channel in lan_channels:
294+ run_command(('bmc-config',
295+ '--lan-channel-number=%d' % channel,
296+ '--commit',
297+ '--filename',
298+ config))
299
300
301 def get_maas_power_settings(user, password, ipaddress, version, boot_type):
302@@ -346,42 +384,45 @@ def main():
303
304 args = parser.parse_args()
305
306+ IPMI_LAN_CHANNELS = get_lan_channels()
307+
308 # Check whether IPMI is being set to DHCP. If it is not, and
309 # '--dhcp-if-static' has been passed, Set it to IPMI to DHCP.
310- if not is_ipmi_dhcp() and args.dhcp:
311- set_ipmi_network_source("Use_DHCP")
312+ if not is_ipmi_dhcp(IPMI_LAN_CHANNELS) and args.dhcp:
313+ set_ipmi_network_source("Use_DHCP", IPMI_LAN_CHANNELS)
314 # allow IPMI 120 seconds to obtain an IP address
315 time.sleep(120)
316
317+ # get the IP address
318+ IPMI_IP_ADDRESS = get_ipmi_ip_address(IPMI_LAN_CHANNELS)
319+ if IPMI_IP_ADDRESS is None:
320+ # if IPMI_IP_ADDRESS not set (or reserved), wait 60 seconds and retry.
321+ set_ipmi_network_source("Static", IPMI_LAN_CHANNELS)
322+ time.sleep(2)
323+ set_ipmi_network_source("Use_DHCP", IPMI_LAN_CHANNELS)
324+ time.sleep(60)
325+ IPMI_IP_ADDRESS = get_ipmi_ip_address(IPMI_LAN_CHANNELS)
326+
327+ if IPMI_IP_ADDRESS is None:
328+ # Exit (to not set power params in MAAS) if no IPMI_IP_ADDRESS
329+ # has been detected
330+ exit(1)
331+
332 # create user/pass
333 IPMI_MAAS_USER = "maas"
334 IPMI_MAAS_PASSWORD = None
335
336- IPMI_MAAS_PASSWORD = configure_ipmi_user(IPMI_MAAS_USER)
337+ IPMI_MAAS_PASSWORD = configure_ipmi_user(IPMI_MAAS_USER, IPMI_LAN_CHANNELS)
338
339 # Attempt to enable IPMI Over Lan. If it is disabled, MAAS won't
340 # be able to remotely communicate to the BMC.
341- set_ipmi_lan_channel_settings()
342+ set_ipmi_lan_channel_settings(IPMI_LAN_CHANNELS)
343
344 # Commit other IPMI settings
345 if args.configdir:
346 for file in os.listdir(args.configdir):
347- commit_ipmi_settings(os.path.join(args.configdir, file))
348-
349- # get the IP address
350- IPMI_IP_ADDRESS = get_ipmi_ip_address()
351- if IPMI_IP_ADDRESS is None:
352- # if IPMI_IP_ADDRESS not set (or reserved), wait 60 seconds and retry.
353- set_ipmi_network_source("Static")
354- time.sleep(2)
355- set_ipmi_network_source("Use_DHCP")
356- time.sleep(60)
357- IPMI_IP_ADDRESS = get_ipmi_ip_address()
358-
359- if IPMI_IP_ADDRESS is None:
360- # Exit (to not set power params in MAAS) if no IPMI_IP_ADDRESS
361- # has been detected
362- exit(1)
363+ commit_ipmi_settings(
364+ os.path.join(args.configdir, file), IPMI_LAN_CHANNELS)
365
366 if bmc_supports_lan2_0():
367 IPMI_VERSION = "LAN_2_0"
368diff --git a/src/metadataserver/user_data/templates/snippets/tests/test_maas_ipmi_autodetect.py b/src/metadataserver/user_data/templates/snippets/tests/test_maas_ipmi_autodetect.py
369index 9d5fc50..4c8584d 100644
370--- a/src/metadataserver/user_data/templates/snippets/tests/test_maas_ipmi_autodetect.py
371+++ b/src/metadataserver/user_data/templates/snippets/tests/test_maas_ipmi_autodetect.py
372@@ -8,6 +8,7 @@ __all__ = []
373 from collections import OrderedDict
374 import os.path
375 import platform
376+import random
377 import re
378 import subprocess
379 from unittest.mock import call
380@@ -88,16 +89,20 @@ class TestBMCKeyPairMethods(MAASTestCase):
381
382 scenarios = [
383 ('bmc_get', dict(
384- method_name='bmc_get', args=['Test:Key'],
385+ method_name='bmc_get',
386+ args=['Test:Key', random.randint(1, 8)],
387 key_pair_fmt='--key-pair=%s', direction='--checkout')),
388 ('bmc_set', dict(
389- method_name='bmc_set', args=['Test:Key', 'myval'],
390+ method_name='bmc_set',
391+ args=['Test:Key', 'myval', random.randint(1, 8)],
392 key_pair_fmt='--key-pair=%s=%s', direction='--commit')),
393 ('bmc_user_get', dict(
394- method_name='bmc_user_get', args=['User10', 'Username'],
395+ method_name='bmc_user_get',
396+ args=['User10', 'Username', random.randint(1, 8)],
397 key_pair_fmt='--key-pair=%s:%s', direction='--checkout')),
398 ('bmc_user_set', dict(
399- method_name='bmc_user_set', args=['User10', 'Username', 'maas'],
400+ method_name='bmc_user_set',
401+ args=['User10', 'Username', 'maas', random.randint(1, 8)],
402 key_pair_fmt='--key-pair=%s:%s=%s', direction='--commit'))
403 ]
404
405@@ -115,9 +120,13 @@ class TestBMCKeyPairMethods(MAASTestCase):
406 # Note that the fmt string must use positional argument specifiers
407 # if the order of appearance of args in the fmt string doesn't match
408 # the order of args to the method.
409- key_pair_string = self.key_pair_fmt % tuple(self.args)
410+ key_pair_string = self.key_pair_fmt % tuple(self.args[:-1])
411+ lan_channel = self.args[-1]
412
413- expected_args = ('bmc-config', self.direction, key_pair_string)
414+ expected_args = ('bmc-config',
415+ '--lan-channel-number=%d' % lan_channel,
416+ self.direction,
417+ key_pair_string)
418 self.assertThat(recorder, MockCalledOnceWith(expected_args))
419
420
421@@ -127,8 +136,13 @@ class TestBMCListSections(MAASTestCase):
422 def test_bmc_list_sections(self):
423 """Ensure bmc-config is called with the correct args."""
424 recorder = self.patch(maas_ipmi_autodetect, 'run_command')
425- bmc_list_sections()
426- self.assertThat(recorder, MockCalledOnceWith(('bmc-config', '-L')))
427+ lan_channel = random.randint(1, 8)
428+ bmc_list_sections(lan_channel)
429+ self.assertThat(
430+ recorder,
431+ MockCalledOnceWith(('bmc-config',
432+ '-L',
433+ '--lan-channel-number=%d' % lan_channel)))
434
435
436 class TestListUserNumbers(MAASTestCase):
437@@ -153,13 +167,13 @@ class TestListUserNumbers(MAASTestCase):
438 self.patch(maas_ipmi_autodetect,
439 'bmc_list_sections').return_value = self.section_names
440 expected = ['User1', 'User4', 'User3', 'User16']
441- user_numbers = list_user_numbers()
442+ user_numbers = list_user_numbers(1)
443 self.assertEqual(expected, user_numbers)
444
445 def test_empty(self):
446 """Ensure an empty list is handled correctly."""
447 self.patch(maas_ipmi_autodetect, 'bmc_list_sections').return_value = ''
448- results = list_user_numbers()
449+ results = list_user_numbers(1)
450 self.assertEqual([], results)
451
452
453@@ -210,7 +224,7 @@ class TestBMCUserGet(MAASTestCase):
454 self.patch(
455 maas_ipmi_autodetect, 'bmc_get').return_value = response
456
457- value = bmc_user_get(user_number, self.key)
458+ value = bmc_user_get(user_number, self.key, 1)
459 self.assertEqual(self.value, value)
460
461
462@@ -290,7 +304,7 @@ class TestPickUserNumberFromList(MAASTestCase):
463 expected=None))
464 ]
465
466- def bmc_user_get(self, user_number, parameter):
467+ def bmc_user_get(self, user_number, parameter, lan_channel):
468 """Return mock user data."""
469 return self.user_attributes[user_number].get(parameter)
470
471@@ -299,7 +313,7 @@ class TestPickUserNumberFromList(MAASTestCase):
472 self.patch(maas_ipmi_autodetect,
473 'bmc_user_get').side_effect = self.bmc_user_get
474 current_users = list(self.user_attributes.keys())
475- user = pick_user_number_from_list('maas', current_users)
476+ user = pick_user_number_from_list('maas', current_users, 1)
477 self.assertEqual(self.expected, user)
478
479
480@@ -312,13 +326,13 @@ class TestPickUserNumber(MAASTestCase):
481 'list_user_numbers').return_value = ['User1', 'User2']
482 self.patch(maas_ipmi_autodetect,
483 'pick_user_number_from_list').return_value = 'User2'
484- user_number = pick_user_number('maas')
485+ user_number = pick_user_number('maas', 1)
486 self.assertEqual('User2', user_number)
487
488 def test_fail_raise_exception(self):
489 """Ensure an exception is raised if no acceptable user is found."""
490 self.patch(maas_ipmi_autodetect, 'list_user_numbers').return_value = []
491- self.assertRaises(IPMIError, pick_user_number, 'maas')
492+ self.assertRaises(IPMIError, pick_user_number, 'maas', 1)
493
494
495 class TestVerifyIpmiUserSettings(MAASTestCase):
496@@ -331,7 +345,7 @@ class TestVerifyIpmiUserSettings(MAASTestCase):
497 expected_settings = {key: value}
498 self.patch(maas_ipmi_autodetect, 'bmc_user_get').return_value = None
499 ipmi_error = self.assertRaises(IPMIError, verify_ipmi_user_settings,
500- 'User2', expected_settings)
501+ 'User2', expected_settings, 1)
502
503 expected_message = (
504 "IPMI user setting verification failures: "
505@@ -355,7 +369,7 @@ class TestVerifyIpmiUserSettings(MAASTestCase):
506
507 self.patch(maas_ipmi_autodetect, 'bmc_user_get').return_value = 'No'
508 ipmi_error = self.assertRaises(IPMIError, verify_ipmi_user_settings,
509- 'User2', expected_settings)
510+ 'User2', expected_settings, 1)
511
512 self.assertRegex(
513 str(ipmi_error),
514@@ -377,7 +391,7 @@ class TestVerifyIpmiUserSettings(MAASTestCase):
515 don't try to verify them.
516 """
517 expected_settings = {'Password': 'bar', 'Enable_User': 'yes'}
518- value = verify_ipmi_user_settings('User2', expected_settings)
519+ value = verify_ipmi_user_settings('User2', expected_settings, 1)
520 self.assertIsNone(value)
521
522
523@@ -392,8 +406,9 @@ class TestApplyIpmiUserSettings(MAASTestCase):
524 self.patch(maas_ipmi_autodetect, 'bmc_user_set')
525 self.patch(maas_ipmi_autodetect, 'verify_ipmi_user_settings')
526 username = 'foo'
527- apply_ipmi_user_settings({'Username': username})
528- self.assertThat(pun_mock, MockCalledOnceWith(username))
529+ lan_channel = random.randint(1, 8)
530+ apply_ipmi_user_settings({'Username': username}, [lan_channel])
531+ self.assertThat(pun_mock, MockCalledOnceWith(username, lan_channel))
532
533 def test_verify_user_settings(self):
534 """Ensure the user settings are committed and verified."""
535@@ -404,14 +419,16 @@ class TestApplyIpmiUserSettings(MAASTestCase):
536 vius_mock = self.patch(maas_ipmi_autodetect,
537 'verify_ipmi_user_settings')
538 user_settings = {'Username': user_number, 'b': 2}
539- apply_ipmi_user_settings(user_settings)
540+ lan_channel = random.randint(1, 8)
541+ apply_ipmi_user_settings(user_settings, [lan_channel])
542
543 for key, value in user_settings.items():
544- self.assertThat(bus_mock, MockAnyCall(user_number, key, value))
545+ self.assertThat(bus_mock,
546+ MockAnyCall(user_number, key, value, lan_channel))
547
548 self.assertThat(
549 vius_mock,
550- MockCalledOnceWith(user_number, user_settings))
551+ MockCalledOnceWith(user_number, user_settings, lan_channel))
552
553 def test_preserves_settings_order(self):
554 """Ensure user settings are applied in order of iteration."""
555@@ -421,9 +438,10 @@ class TestApplyIpmiUserSettings(MAASTestCase):
556 bus_mock = self.patch(maas_ipmi_autodetect, 'bmc_user_set')
557 self.patch(maas_ipmi_autodetect, 'verify_ipmi_user_settings')
558 user_settings = OrderedDict((('Username', 1), ('b', 2), ('c', 3)))
559- apply_ipmi_user_settings(user_settings)
560+ lan_channel = random.randint(1, 8)
561+ apply_ipmi_user_settings(user_settings, [lan_channel])
562 expected_calls = (
563- call(user_number, key, value)
564+ call(user_number, key, value, lan_channel)
565 for key, value in user_settings.items()
566 )
567 self.assertThat(bus_mock, MockCallsMatch(*expected_calls))
568@@ -459,8 +477,9 @@ class TestConfigureIPMIUser(MAASTestCase):
569 self.patch(maas_ipmi_autodetect,
570 'make_ipmi_user_settings').return_value = expected.copy()
571 recorder = self.patch(maas_ipmi_autodetect, 'apply_ipmi_user_settings')
572- configure_ipmi_user('DC')
573- self.assertThat(recorder, MockCalledOnceWith(expected))
574+ lan_channel = random.randint(1, 8)
575+ configure_ipmi_user('DC', [lan_channel])
576+ self.assertThat(recorder, MockCalledOnceWith(expected, [lan_channel]))
577
578 def test_configures_user_with_standard_password(self):
579 """Test that it returns the configured password if successful"""
580@@ -468,7 +487,7 @@ class TestConfigureIPMIUser(MAASTestCase):
581 self.patch(maas_ipmi_autodetect,
582 'generate_random_password').return_value = password
583 self.patch(maas_ipmi_autodetect, 'apply_ipmi_user_settings')
584- configured_password = configure_ipmi_user('DC')
585+ configured_password = configure_ipmi_user('DC', [random.randint(1, 8)])
586 self.assertEqual(configured_password, password)
587
588 def test_raises_ipmi_error_if_cant_configure_user(self):
589@@ -479,7 +498,7 @@ class TestConfigureIPMIUser(MAASTestCase):
590 1, 'bmc-set')
591 self.patch(maas_ipmi_autodetect, 'configure_ipmi_user')
592 self.assertRaises(
593- IPMIError, configure_ipmi_user, 'maas')
594+ IPMIError, configure_ipmi_user, 'maas', [random.randint(1, 8)])
595
596
597 class TestGeneratesAcceptablePasswords(MAASTestCase):
598@@ -542,9 +561,14 @@ class TestCommitIPMISettings(MAASTestCase):
599 """Ensure bmc-config is run properly."""
600 recorder = self.patch(maas_ipmi_autodetect, 'run_command')
601 filename = 'foo'
602- commit_ipmi_settings(filename)
603+ lan_channel = random.randint(1, 8)
604+ commit_ipmi_settings(filename, [lan_channel])
605 self.assertThat(recorder, MockCalledOnceWith(
606- ('bmc-config', '--commit', '--filename', filename)))
607+ ('bmc-config',
608+ '--lan-channel-number=%d' % lan_channel,
609+ '--commit',
610+ '--filename',
611+ filename)))
612
613
614 class TestBMCSupportsLANPlus(MAASTestCase):
615@@ -640,13 +664,33 @@ class TestGetIPMIIPAddress(MAASTestCase):
616 'Lan6_Conf:IPv6_Dynamic_Addresses': self.output_dy
617 }
618
619- def ret_val(arg):
620+ def ret_val(arg, lan_channel):
621+ return ret_values[arg]
622+
623+ self.patch(
624+ maas_ipmi_autodetect,
625+ '_bmc_get_ipmi_addresses').side_effect = ret_val
626+ actual = get_ipmi_ip_address([random.randint(1, 8)])
627+ self.assertEqual(self.expected, actual)
628+
629+ def test_get_ipmi_ip_address_multiple_channels(self):
630+ ret_values = {
631+ 'Lan_Conf:IP_Address': self.output4,
632+ 'Lan6_Conf:IPv6_Static_Addresses': self.output_st,
633+ 'Lan6_Conf:IPv6_Dynamic_Addresses': self.output_dy
634+ }
635+
636+ connected_lan_channel = random.randint(2, 8)
637+
638+ def ret_val(arg, lan_channel):
639+ if lan_channel != connected_lan_channel:
640+ return ""
641 return ret_values[arg]
642
643 self.patch(
644 maas_ipmi_autodetect,
645 '_bmc_get_ipmi_addresses').side_effect = ret_val
646- actual = get_ipmi_ip_address()
647+ actual = get_ipmi_ip_address([1, connected_lan_channel])
648 self.assertEqual(self.expected, actual)
649
650
651@@ -668,18 +712,23 @@ class TestEnableLanChannel(MAASTestCase):
652 bmc_set_mock = self.patch(
653 maas_ipmi_autodetect, 'bmc_set')
654
655+ lan_channel = random.randint(1, 8)
656 # Call the function
657- set_ipmi_lan_channel_settings()
658+ set_ipmi_lan_channel_settings([lan_channel])
659
660 # Check that the 'bmc_set_mock' was called
661 self.assertThat(
662 bmc_set_mock,
663 MockAnyCall(
664- 'Lan_Channel:Volatile_Access_Mode', 'Always_Available'))
665+ 'Lan_Channel:Volatile_Access_Mode',
666+ 'Always_Available',
667+ lan_channel))
668 self.assertThat(
669 bmc_set_mock,
670 MockAnyCall(
671- 'Lan_Channel:Non_Volatile_Access_Mode', 'Always_Available'))
672+ 'Lan_Channel:Non_Volatile_Access_Mode',
673+ 'Always_Available',
674+ lan_channel))
675
676 def test_dont_enable_lan_channel_if_already_enabled(self):
677 """Test that Lan_Channel doesn't get enabled if disabled."""
678@@ -698,7 +747,7 @@ class TestEnableLanChannel(MAASTestCase):
679 maas_ipmi_autodetect, 'bmc_set')
680
681 # Call the function
682- set_ipmi_lan_channel_settings()
683+ set_ipmi_lan_channel_settings([random.randint(1, 8)])
684
685 # Check that the 'bmc_set' mock function (bmc_set_mock) was not called.
686 self.assertThat(
687@@ -719,7 +768,7 @@ class TestEnableLanChannel(MAASTestCase):
688 maas_ipmi_autodetect, 'bmc_set')
689 bmc_set_mock.side_effect = subprocess.CalledProcessError(
690 1, 'bmc-set')
691- set_ipmi_lan_channel_settings()
692+ set_ipmi_lan_channel_settings([random.randint(1, 8)])
693 self.assertRaises(
694 subprocess.CalledProcessError, bmc_set_mock)
695 self.assertThat(

Subscribers

People subscribed via source and target branches