Merge ~cberner/maas/+git/maas:lan_channel into maas:master
- Git
- lp:~cberner/maas/+git/maas
- lan_channel
- Merge into master
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) |
Related bugs: |
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 |
Commit message
Description of the change
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_
bin/test.region metadataserver.
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.
Mike Pontillo (mpontillo) wrote : | # |
To add to what Newell said, if you run 'make install-
- f2fd477... by Christopher Berner
-
Fix tests
- ca7718f... by Christopher Berner
-
Add test for multiple LAN channels
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.
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://
COMMIT: ca7718f5add13cc
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
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.
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-
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)
- 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
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.
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: bd5ce06535ef54f
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: 08281c34e3ff074
Newell Jensen (newell-jensen) wrote : | # |
See comment/suggestion inline.
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
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.
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
1 | diff --git a/src/metadataserver/user_data/templates/snippets/maas_ipmi_autodetect.py b/src/metadataserver/user_data/templates/snippets/maas_ipmi_autodetect.py |
2 | index 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" |
368 | diff --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 |
369 | index 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( |
UNIT TESTS
-b lan_channel lp:~cberner/maas into -b master lp:~maas-committers/maas
STATUS: FAILED maas-ci- jenkins. internal: 8080/job/ maas/job/ branch- tester/ 1231/console 397778c757173d5 e7798d2763
LOG: http://
COMMIT: 0388bec1e989356