summaryrefslogtreecommitdiffstats
path: root/fuel/deploy/hardware_adapters/hp/hp_adapter.py
blob: 7ce0dc9bcf7348c42fd59444b969865cc77800e3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
import re
import time
from netaddr import EUI, mac_unix
import logging

from run_oa_command import RunOACommand


LOG = logging.getLogger(__name__)
out_hdlr = logging.FileHandler(__file__.split('.')[0] + '.log', mode='w')
out_hdlr.setFormatter(logging.Formatter('%(levelname)s: %(message)s'))
LOG.addHandler(out_hdlr)
LOG.setLevel(logging.DEBUG)

class HpAdapter(object):

    # Exception thrown at any kind of failure to get the requested
    # information.
    class NoInfoFoundError(Exception):
        pass

    # Totally failed to connect so a re-try with other HW should
    # be done. This exception should never escape this class.
    class InternalConnectError(Exception):
        pass

    # Format MAC so leading zeroes are displayed
    class mac_dhcp(mac_unix):
        word_fmt = "%.2x"

    def __init__(self, mgmt_ip, username, password):
        self.mgmt_ip = mgmt_ip
        self.username = username
        self.password = password
        self.oa_error_message = ''

    def get_blade_mac_addresses(self, shelf, blade):

        LOG.debug("Entering: get_mac_addr_hp(%d,%d)" % (shelf, blade))
        self.oa_error_message = ''
        oa = RunOACommand(self.mgmt_ip, self.username, self.password)

        LOG.debug("Connect to active OA for shelf %d" % shelf)
        try:
            res = oa.connect_to_active()
        except:
            raise self.InternalConnectError(oa.error_message)
        if res is None:
            raise self.InternalConnectError(oa.error_message)
        if not oa.connected():
            raise self.NoInfoFoundError(oa.error_message)

        cmd = ("show server info " + str(blade))

        LOG.debug("Send command to OA: %s" % cmd)
        try:
            serverinfo = oa.send_command(cmd)
        except:
            raise self.NoInfoFoundError(oa.error_message)
        finally:
            oa.close()

        (left, right) = self.find_mac(serverinfo, shelf, blade)

        left = EUI(left, dialect=self.mac_dhcp)
        right = EUI(right, dialect=self.mac_dhcp)
        return [str(left), str(right)]

    def get_blade_hardware_info(self, shelf, blade=None):

        if blade:
            LOG.debug("Entering: get_hp_info(%d,%d)" % (shelf, blade))
        else:
            LOG.debug("Entering: get_hp_info(%d)" % shelf)

        self.oa_error_message = ''
        oa = RunOACommand(self.mgmt_ip, self.username, self.password)

        LOG.debug("Connect to active OA for shelf %d" % shelf)

        try:
            res = oa.connect_to_active()
        except:
            self.oa_error_message = oa.error_message
            return None
        if res is None:
            self.oa_error_message = oa.error_message
            return None
        if not oa.connected():
            self.oa_error_message = oa.error_message
            return None

        # If no blade specified we're done we know this is an HP at this point
        if not blade:
            oa.close()
            return "HP"

        check = "show server info %d" % blade
        LOG.debug("Send command to OA: %s" % check)
        output = oa.send_command("%s" % check)
        oa.close()

        match = r"Product Name:\s+(.+)\Z"
        if re.search(match, str(output[:])) is None:
            self.oa_error_message = ("Blade %d in shelf %d does not exist\n"
                                     % (blade, shelf))
            return None

        for line in output:
            seobj = re.search(match, line)
            if seobj:
                return "HP %s" % seobj.group(1)
        return False

    def power_off_blades(self, shelf, blade_list):
        return self.set_state(shelf, 'locked', blade_list=blade_list)

    def power_on_blades(self, shelf, blade_list):
        return self.set_state(shelf, 'unlocked', blade_list=blade_list)

    def power_off_blade(self, shelf, blade):
        return self.set_state(shelf, 'locked', one_blade=blade)

    def power_on_blade(self, shelf, blade):
        return self.set_state(shelf, 'unlocked', one_blade=blade)

    def set_boot_order_blade(self, shelf, blade):
        return self.set_boot_order(shelf, one_blade=blade)

    def set_boot_order_blades(self, shelf, blade_list):
        return self.set_boot_order(shelf, blade_list=blade_list)



    # Search HP's OA server info for MAC for left and right control
    def find_mac(self, serverinfo, shelf, blade):
        left = False
        right = False
        for line in serverinfo:
            if ("No Server Blade Installed" in line or
                    "Invalid Arguments" in line):
                raise self.NoInfoFoundError("Blade %d in shelf %d "
                                            "does not exist." % (blade, shelf))
            seobj = re.search(r"LOM1:1-a\s+([0-9A-F:]+)", line, re.I)
            if seobj:
                left = seobj.group(1)
            else:
                seobj = re.search(r"LOM1:2-a\s+([0-9A-F:]+)", line, re.I)
                if seobj:
                    right = seobj.group(1)
            if left and right:
                return left, right
        raise self.NoInfoFoundError("Could not find MAC for blade %d "
                                    "in shelf %d." % (blade, shelf))

    # Do power on or off on all configured blades in shelf
    # Return None to indicate that no connection do OA succeeded,
    # Return False to indicate some connection to OA succeeded,
    # or config error
    # Return True to indicate that power state succesfully updated
    # state: locked, unlocked
    def set_state(self, shelf, state, one_blade=None, blade_list=None):

        if state not in ['locked', 'unlocked']:
            return None

        if one_blade:
            LOG.debug("Entering: set_state_hp(%d,%s,%d)" %
                      (shelf, state, one_blade))
        else:
            LOG.debug("Entering: set_state_hp(%d,%s)" % (shelf, state))

        self.oa_error_message = ''

        oa = RunOACommand(self.mgmt_ip, self.username, self.password)

        LOG.debug("Connect to active OA for shelf %d" % shelf)

        try:
            res = oa.connect_to_active()
        except:
            self.oa_error_message = oa.error_message
            return None
        if res is None:
            self.oa_error_message = oa.error_message
            return None
        if not oa.connected():
            self.oa_error_message = oa.error_message
            return False

        if one_blade:
            blades = [one_blade]
        else:
            blades = sorted(blade_list)

        LOG.debug("Check if blades are present")

        check = "show server list"

        LOG.debug("Send command to OA: %s" % check)
        output = oa.send_command(check)
        first = True
        bladelist = ''
        for blade in blades:
            prog = re.compile(r"\s+" + str(blade) + r"\s+\[Absent\]",
                              re.MULTILINE)
            if prog.search(str(output[:])) is not None:
                oa.close()
                self.oa_error_message = ("Blade %d in shelf %d "
                                         % (blade, shelf))
                if one_blade:
                    self.oa_error_message += ("does not exist.\n"
                                         "Set state %s not performed.\n"
                                         % state)
                else:
                    self.oa_error_message += (
                        "specified but does not exist.\nSet "
                        "state %s not performed on shelf %d\n"
                        % (state, shelf))
                return False
            if not first:
                bladelist += ","
            else:
                first = False
            bladelist += str(blade)

        if blade_list:
            LOG.debug("All blades present")

        # Use leading upper case on On/Off so it can be reused in match
        extra = ""
        if state == "locked":
            powerstate = "Off"
            extra = "force"
        else:
            powerstate = "On"

        cmd = "power%s server %s" % (powerstate, bladelist)

        if extra != "":
            cmd += " %s" % extra

        LOG.debug("Send command to OA: %s" % cmd)

        try:
            oa.send_command(cmd)
        except:
            self.oa_error_message = oa.error_message
            oa.close()
            return False

        # Check that all blades reach the state which can take some time,
        # so re-try a couple of times
        LOG.debug("Check if state %s successfully set" % state)
        recheck = 2
        while True:
            LOG.debug("Send command to OA: %s" % check)
            try:
                output = oa.send_command(check)
            except:
                self.oa_error_message = oa.error_message
                oa.close()
                return False
            for blade in blades:
                match = (r"\s+" + str(blade) +
                         r"\s+\w+\s+\w+.\w+.\w+.\w+\s+\w+\s+%s" %
                         powerstate)
                prog = re.compile(match, re.MULTILINE)
                if prog.search(str(output[:])) is None:
                    recheck -= 1
                    if recheck >= 0:
                        # Re-try
                        time.sleep(3)
                        break
                    oa.close()
                    self.oa_error_message = (
                        "Could not set state %s on blade %d in shelf %d\n"
                        % (state, one_blade, shelf))
                    for line in output:
                        self.oa_error_message += line
                    return False
            else:
                # state reached for all blades, exit the infinite loop
                break

        if one_blade:
            LOG.debug("State %s successfully set on blade %d in shelf %d"
                      % (state, one_blade, shelf))
        else:
            LOG.debug("State %s successfully set on blades %s in shelf %d"
                      % (state, blade_list, shelf))
        oa.close()
        return True

    # Change boot order on all blades in shelf
    # Return None to indicate that no connection do OA succeeded,
    # Return False to indicate some connection to OA succeeded,
    # or config error,
    # Return True to indicate that boot order succesfully changed
    def set_boot_order(self, shelf, one_blade=None, blade_list=None):

        if one_blade:
            LOG.debug("Entering: set_bootorder_hp(%d,%d)" % (shelf, one_blade))
        else:
            LOG.debug("Entering: set_bootorder_hp(%d)" % shelf)

        self.oa_error_message = ''

        oa = RunOACommand(self.mgmt_ip, self.username, self.password)

        LOG.debug("Connect to active OA for shelf %d" % shelf)

        try:
            res = oa.connect_to_active()
        except:
            self.oa_error_message = oa.error_message
            return None
        if res is None:
            self.oa_error_message = oa.error_message
            return None
        if not oa.connected():
            self.oa_error_message = oa.error_message
            return False

        if one_blade:
            blades = [one_blade]
        else:
            blades = sorted(blade_list)

        LOG.debug("Check if blades are present")

        check = "show server list"

        LOG.debug("Send command to OA: %s" % check)

        output = oa.send_command(check)
        first = True
        bladelist = ''
        for blade in blades:
            prog = re.compile(r"\s+" + str(blade) + r"\s+\[Absent\]",
                              re.MULTILINE)
            if prog.search(str(output[:])) is not None:
                oa.close()
                self.oa_error_message = ("Blade %d in shelf %d "
                                         % (blade, shelf))
                if one_blade:
                    self.oa_error_message += (
                        "does not exist.\nChange boot order not performed.\n")
                else:
                    self.oa_error_message += (
                        "specified but does not exist.\n"
                        "Change boot order not performed on shelf %d\n"
                        % shelf)
                return False
            if not first:
                bladelist += ','
            else:
                first = False
            bladelist += str(blade)

        if blade_list:
            LOG.debug("All blades present")

        # Boot origins are pushed so first set boot from hard disk, then PXE
        # NB! If we want to support boot from SD we must add USB to the "stack"
        cmd1 = "set server boot first hdd %s" % bladelist
        cmd2 = "set server boot first pxe %s" % bladelist
        for cmd in [cmd1, cmd2]:

            LOG.debug("Send command to OA: %s" % cmd)
            try:
                output = oa.send_command(cmd)
            except:
                self.oa_error_message = oa.error_message
                for line in output:
                    self.oa_error_message += line
                oa.close()
                return False

        # Check that all blades got the correct boot order
        # Needs updating if USB is added
        LOG.debug("Check if boot order successfully set")
        match = (r"^.*Boot Order\):\',\s*\'(\\t)+PXE NIC 1\',\s*\'(\\t)"
                 r"+Hard Drive")
        prog = re.compile(match)
        for blade in blades:

            check = "show server boot %d" % blade

            LOG.debug("Send command to OA: %s" % check)
            try:
                output = oa.send_command(check)
            except:
                self.oa_error_message = oa.error_message
                oa.close()
                return False
            if prog.search(str(output[:])) is None:
                oa.close()
                self.oa_error_message = ("Failed to set boot order on blade "
                                         "%d in shelf %d\n" % (blade, shelf))
                for line in output:
                    self.oa_error_message += line
                return False
            LOG.debug("Boot order successfully set on blade %d in shelf %d"
                      % (blade, shelf))

        if blade_list:
            LOG.debug("Boot order successfully set on all configured blades "
                      "in shelf %d" % (shelf))
        oa.close()
        return True