~weii666/ubuntu/trusty/maas/fix-for-1325167

« back to all changes in this revision

Viewing changes to etc/maas/templates/commissioning-user-data/snippets/maas_ipmi_autodetect.py

  • Committer: Package Import Robot
  • Author(s): Andres Rodriguez
  • Date: 2014-03-04 18:55:01 UTC
  • Revision ID: package-import@ubuntu.com-20140304185501-w4gf9xd67ijemfub
Tags: 1.5+bzr1977-0ubuntu3
debian/patches/04-maas-ipmi-fixes.patch: Fix IPMI detection in trusty,
which is currently broken due to upstream changes.

Show diffs side-by-side

added added

removed removed

Lines of Context:
38
38
import time
39
39
 
40
40
 
 
41
class IPMIError(Exception):
 
42
    """An error related to IPMI."""
 
43
 
 
44
 
41
45
def run_command(command_args):
42
46
    """Run a command. Return output if successful or raise exception if not."""
43
47
    output = subprocess.check_output(command_args, stderr=subprocess.STDOUT)
65
69
def bmc_user_get(user_number, parameter):
66
70
    """Get a user parameter via bmc-config commit."""
67
71
    key = format_user_key(user_number, parameter)
68
 
    return bmc_get(key)
 
72
    raw = bmc_get(key)
 
73
    pattern = r'^\s*%s(?:[ \t])+([^#\s]+[^\n]*)$' % (re.escape(parameter))
 
74
    match = re.search(pattern, raw, re.MULTILINE)
 
75
    if match is None:
 
76
        return None
 
77
    return match.group(1)
69
78
 
70
79
 
71
80
def bmc_user_set(user_number, parameter, value):
74
83
    bmc_set(key, value)
75
84
 
76
85
 
 
86
def bmc_list_sections():
 
87
    """Retrieve the names of config sections from the BMC."""
 
88
    command = ('bmc-config', '-L')
 
89
    output = run_command(command)
 
90
    return output
 
91
 
 
92
 
 
93
def list_user_numbers():
 
94
    """List the user numbers on the BMC."""
 
95
    output = bmc_list_sections()
 
96
    pattern = r'^(User\d+)$'
 
97
    users = re.findall(pattern, output, re.MULTILINE)
 
98
 
 
99
    return users
 
100
 
 
101
 
 
102
def pick_user_number_from_list(search_username, user_numbers):
 
103
    """Pick the best user number for a user from a list of user numbers.
 
104
 
 
105
    If any any existing user's username matches the search username, pick
 
106
    that user.
 
107
 
 
108
    Otherwise, pick the first user that has no username set.
 
109
 
 
110
    If no users match those criteria, raise an IPMIError.
 
111
    """
 
112
    first_unused = None
 
113
 
 
114
    for user_number in user_numbers:
 
115
        # The IPMI spec reserves User1 as anonymous.
 
116
        if user_number == 'User1':
 
117
            continue
 
118
 
 
119
        username = bmc_user_get(user_number, 'Username')
 
120
 
 
121
        if username == search_username:
 
122
            return user_number
 
123
 
 
124
        # Usually a BMC won't include a Username value if the user is unused.
 
125
        # Some HP BMCs use "(Empty User)" to indicate a user in unused.
 
126
        if username in [None, '(Empty User)'] and first_unused is None:
 
127
            first_unused = user_number
 
128
 
 
129
    return first_unused
 
130
 
 
131
 
 
132
def pick_user_number(search_username):
 
133
    """Pick the best user number for a username."""
 
134
    user_numbers = list_user_numbers()
 
135
    user_number = pick_user_number_from_list(search_username, user_numbers)
 
136
 
 
137
    if not user_number:
 
138
        raise IPMIError('No IPMI user slots available.')
 
139
 
 
140
    return user_number
 
141
 
 
142
 
77
143
def is_ipmi_dhcp():
78
144
    output = bmc_get('Lan_Conf:IP_Address_Source')
79
145
    show_re = re.compile('IP_Address_Source\s+Use_DHCP')
91
157
    return res.group()
92
158
 
93
159
 
94
 
def get_ipmi_user_number(user):
95
 
    for i in range(1, 17):
96
 
        ipmi_user_number = "User%s" % i
97
 
        # bmc-config fails if no slot for the requested user exists;
98
 
        # instead of bailing, just keep trying remaining users.
99
 
        try:
100
 
            output = bmc_user_get(ipmi_user_number, 'Username')
101
 
        except subprocess.CalledProcessError:
102
 
            pass
103
 
        else:
104
 
            if user in output:
105
 
                return ipmi_user_number
106
 
    return None
107
 
 
108
 
 
109
 
def commit_ipmi_user_settings(user, password):
110
 
    ipmi_user_number = get_ipmi_user_number(user)
111
 
    if ipmi_user_number is None:
112
 
        bmc_user_set('User10', 'Username', user)
113
 
        ipmi_user_number = get_ipmi_user_number(user)
114
 
    bmc_user_set(ipmi_user_number, 'Username', user)
115
 
    bmc_user_set(ipmi_user_number, 'Password', password)
116
 
    bmc_user_set(ipmi_user_number, 'Enable_User', 'Yes')
117
 
    bmc_user_set(ipmi_user_number, 'Lan_Enable_IPMI_Msgs', 'Yes')
118
 
    bmc_user_set(ipmi_user_number, 'Lan_Privilege_Limit', 'Administrator')
 
160
def verify_ipmi_user_settings(user_number, user_settings):
 
161
    """Verify user settings were applied correctly."""
 
162
 
 
163
    bad_values = {}
 
164
 
 
165
    for key, expected_value in user_settings.iteritems():
 
166
        # Password isn't included in checkout.
 
167
        if key == 'Password':
 
168
            continue
 
169
 
 
170
        actual_value = bmc_user_get(user_number, key)
 
171
        if expected_value != actual_value:
 
172
            bad_values[key] = actual_value
 
173
 
 
174
    if len(bad_values) == 0:
 
175
        return
 
176
 
 
177
    errors_string = ' '.join([
 
178
        "for '%s', expected '%s', actual '%s';" % (
 
179
            key, user_settings[key], actual_value)
 
180
        for key, actual_value in bad_values.iteritems()
 
181
    ]).rstrip(';')
 
182
    message = 'IPMI user setting verification failures: %s.' % (errors_string)
 
183
    raise IPMIError(message)
 
184
 
 
185
 
 
186
def apply_ipmi_user_settings(user_settings):
 
187
    """Commit and verify IPMI user settings."""
 
188
    username = user_settings['Username']
 
189
    ipmi_user_number = pick_user_number(username)
 
190
 
 
191
    for key, value in user_settings.iteritems():
 
192
        bmc_user_set(ipmi_user_number, key, value)
 
193
 
 
194
    verify_ipmi_user_settings(ipmi_user_number, user_settings)
119
195
 
120
196
 
121
197
def commit_ipmi_settings(config):
122
 
    run_command(('bmc-config', '--commit', '--filename %s') % config)
 
198
    run_command(('bmc-config', '--commit', '--filename', config))
123
199
 
124
200
 
125
201
def get_maas_power_settings(user, password, ipaddress, version):
142
218
    return ''.join([random.choice(letters) for _ in range(length)])
143
219
 
144
220
 
145
 
def get_ipmi_version():
 
221
def bmc_supports_lan2_0():
 
222
    """Detect if BMC supports LAN 2.0."""
146
223
    output = run_command(('ipmi-locate'))
147
 
    #IPMI Version: 2.0
148
 
    #IPMI locate driver: SMBIOS
149
 
    #IPMI interface: KCS
150
 
    #BMC driver device:
151
 
    #BMC I/O base address: 0xCA2
152
 
    #Register spacing: 1
153
 
    #show_re = re.compile('(IPMI\ Version:) (\d\.\d)')
154
 
    show_re = re.compile(
155
 
        '(IPMI\ Version:) (\d\.\d)(\n)(.*)(\n)(.*)(\n)(.*)(\n)'
156
 
        '(BMC\ I\/O\ base\ address:) (0xCA2)')
157
 
    res = show_re.search(output)
158
 
    if res is None:
159
 
        return
160
 
    return res.group(2)
 
224
    return 'IPMI Version: 2.0' in output
161
225
 
162
226
 
163
227
def main():
183
247
        set_ipmi_network_source("Use_DHCP")
184
248
        # allow IPMI 120 seconds to obtain an IP address
185
249
        time.sleep(120)
186
 
 
187
250
    # create user/pass
188
251
    IPMI_MAAS_USER = "maas"
189
252
    IPMI_MAAS_PASSWORD = generate_random_password()
190
253
 
191
254
    # Configure IPMI user/password
192
 
    commit_ipmi_user_settings(IPMI_MAAS_USER, IPMI_MAAS_PASSWORD)
 
255
 
 
256
    user_settings = {
 
257
        'Username': IPMI_MAAS_USER,
 
258
        'Password': IPMI_MAAS_PASSWORD,
 
259
        'Enable_User': 'Yes',
 
260
        'Lan_Enable_IPMI_Msgs': 'Yes',
 
261
        'Lan_Privilege_Limit': 'Administrator'
 
262
    }
 
263
 
 
264
    apply_ipmi_user_settings(user_settings)
193
265
 
194
266
    # Commit other IPMI settings
195
267
    if args.configdir:
211
283
        # has been detected
212
284
        exit(1)
213
285
 
214
 
    IPMI_VERSION = "LAN"
215
 
    if get_ipmi_version() == "2.0":
 
286
    if bmc_supports_lan2_0():
216
287
        IPMI_VERSION = "LAN_2_0"
 
288
    else:
 
289
        IPMI_VERSION = "LAN"
217
290
    if args.commission_creds:
218
291
        print(get_maas_power_settings_json(
219
292
            IPMI_MAAS_USER, IPMI_MAAS_PASSWORD, IPMI_IP_ADDRESS, IPMI_VERSION))