Merge "Allow deployment on Centos 6.5 environment"
[genesis.git] / fuel / deploy / hardware_adapters / hp / hp_adapter.py
1 import re
2 import time
3 from netaddr import EUI, mac_unix
4 import logging
5
6 from run_oa_command import RunOACommand
7
8
9 LOG = logging.getLogger(__name__)
10 out_hdlr = logging.FileHandler(__file__.split('.')[0] + '.log', mode='w')
11 out_hdlr.setFormatter(logging.Formatter('%(levelname)s: %(message)s'))
12 LOG.addHandler(out_hdlr)
13 LOG.setLevel(logging.DEBUG)
14
15 class HpAdapter(object):
16
17     # Exception thrown at any kind of failure to get the requested
18     # information.
19     class NoInfoFoundError(Exception):
20         pass
21
22     # Totally failed to connect so a re-try with other HW should
23     # be done. This exception should never escape this class.
24     class InternalConnectError(Exception):
25         pass
26
27     # Format MAC so leading zeroes are displayed
28     class mac_dhcp(mac_unix):
29         word_fmt = "%.2x"
30
31     def __init__(self, mgmt_ip, username, password):
32         self.mgmt_ip = mgmt_ip
33         self.username = username
34         self.password = password
35         self.oa_error_message = ''
36
37     def get_blade_mac_addresses(self, shelf, blade):
38
39         LOG.debug("Entering: get_mac_addr_hp(%d,%d)" % (shelf, blade))
40         self.oa_error_message = ''
41         oa = RunOACommand(self.mgmt_ip, self.username, self.password)
42
43         LOG.debug("Connect to active OA for shelf %d" % shelf)
44         try:
45             res = oa.connect_to_active()
46         except:
47             raise self.InternalConnectError(oa.error_message)
48         if res is None:
49             raise self.InternalConnectError(oa.error_message)
50         if not oa.connected():
51             raise self.NoInfoFoundError(oa.error_message)
52
53         cmd = ("show server info " + str(blade))
54
55         LOG.debug("Send command to OA: %s" % cmd)
56         try:
57             serverinfo = oa.send_command(cmd)
58         except:
59             raise self.NoInfoFoundError(oa.error_message)
60         finally:
61             oa.close()
62
63         (left, right) = self.find_mac(serverinfo, shelf, blade)
64
65         left = EUI(left, dialect=self.mac_dhcp)
66         right = EUI(right, dialect=self.mac_dhcp)
67         return [str(left), str(right)]
68
69     def get_blade_hardware_info(self, shelf, blade=None):
70
71         if blade:
72             LOG.debug("Entering: get_hp_info(%d,%d)" % (shelf, blade))
73         else:
74             LOG.debug("Entering: get_hp_info(%d)" % shelf)
75
76         self.oa_error_message = ''
77         oa = RunOACommand(self.mgmt_ip, self.username, self.password)
78
79         LOG.debug("Connect to active OA for shelf %d" % shelf)
80
81         try:
82             res = oa.connect_to_active()
83         except:
84             self.oa_error_message = oa.error_message
85             return None
86         if res is None:
87             self.oa_error_message = oa.error_message
88             return None
89         if not oa.connected():
90             self.oa_error_message = oa.error_message
91             return None
92
93         # If no blade specified we're done we know this is an HP at this point
94         if not blade:
95             oa.close()
96             return "HP"
97
98         check = "show server info %d" % blade
99         LOG.debug("Send command to OA: %s" % check)
100         output = oa.send_command("%s" % check)
101         oa.close()
102
103         match = r"Product Name:\s+(.+)\Z"
104         if re.search(match, str(output[:])) is None:
105             self.oa_error_message = ("Blade %d in shelf %d does not exist\n"
106                                      % (blade, shelf))
107             return None
108
109         for line in output:
110             seobj = re.search(match, line)
111             if seobj:
112                 return "HP %s" % seobj.group(1)
113         return False
114
115     def power_off_blades(self, shelf, blade_list):
116         return self.set_state(shelf, 'locked', blade_list=blade_list)
117
118     def power_on_blades(self, shelf, blade_list):
119         return self.set_state(shelf, 'unlocked', blade_list=blade_list)
120
121     def power_off_blade(self, shelf, blade):
122         return self.set_state(shelf, 'locked', one_blade=blade)
123
124     def power_on_blade(self, shelf, blade):
125         return self.set_state(shelf, 'unlocked', one_blade=blade)
126
127     def set_boot_order_blade(self, shelf, blade):
128         return self.set_boot_order(shelf, one_blade=blade)
129
130     def set_boot_order_blades(self, shelf, blade_list):
131         return self.set_boot_order(shelf, blade_list=blade_list)
132
133
134
135     # Search HP's OA server info for MAC for left and right control
136     def find_mac(self, serverinfo, shelf, blade):
137         left = False
138         right = False
139         for line in serverinfo:
140             if ("No Server Blade Installed" in line or
141                     "Invalid Arguments" in line):
142                 raise self.NoInfoFoundError("Blade %d in shelf %d "
143                                             "does not exist." % (blade, shelf))
144             seobj = re.search(r"LOM1:1-a\s+([0-9A-F:]+)", line, re.I)
145             if seobj:
146                 left = seobj.group(1)
147             else:
148                 seobj = re.search(r"LOM1:2-a\s+([0-9A-F:]+)", line, re.I)
149                 if seobj:
150                     right = seobj.group(1)
151             if left and right:
152                 return left, right
153         raise self.NoInfoFoundError("Could not find MAC for blade %d "
154                                     "in shelf %d." % (blade, shelf))
155
156     # Do power on or off on all configured blades in shelf
157     # Return None to indicate that no connection do OA succeeded,
158     # Return False to indicate some connection to OA succeeded,
159     # or config error
160     # Return True to indicate that power state succesfully updated
161     # state: locked, unlocked
162     def set_state(self, shelf, state, one_blade=None, blade_list=None):
163
164         if state not in ['locked', 'unlocked']:
165             return None
166
167         if one_blade:
168             LOG.debug("Entering: set_state_hp(%d,%s,%d)" %
169                       (shelf, state, one_blade))
170         else:
171             LOG.debug("Entering: set_state_hp(%d,%s)" % (shelf, state))
172
173         self.oa_error_message = ''
174
175         oa = RunOACommand(self.mgmt_ip, self.username, self.password)
176
177         LOG.debug("Connect to active OA for shelf %d" % shelf)
178
179         try:
180             res = oa.connect_to_active()
181         except:
182             self.oa_error_message = oa.error_message
183             return None
184         if res is None:
185             self.oa_error_message = oa.error_message
186             return None
187         if not oa.connected():
188             self.oa_error_message = oa.error_message
189             return False
190
191         if one_blade:
192             blades = [one_blade]
193         else:
194             blades = sorted(blade_list)
195
196         LOG.debug("Check if blades are present")
197
198         check = "show server list"
199
200         LOG.debug("Send command to OA: %s" % check)
201         output = oa.send_command(check)
202         first = True
203         bladelist = ''
204         for blade in blades:
205             prog = re.compile(r"\s+" + str(blade) + r"\s+\[Absent\]",
206                               re.MULTILINE)
207             if prog.search(str(output[:])) is not None:
208                 oa.close()
209                 self.oa_error_message = ("Blade %d in shelf %d "
210                                          % (blade, shelf))
211                 if one_blade:
212                     self.oa_error_message += ("does not exist.\n"
213                                          "Set state %s not performed.\n"
214                                          % state)
215                 else:
216                     self.oa_error_message += (
217                         "specified but does not exist.\nSet "
218                         "state %s not performed on shelf %d\n"
219                         % (state, shelf))
220                 return False
221             if not first:
222                 bladelist += ","
223             else:
224                 first = False
225             bladelist += str(blade)
226
227         if blade_list:
228             LOG.debug("All blades present")
229
230         # Use leading upper case on On/Off so it can be reused in match
231         extra = ""
232         if state == "locked":
233             powerstate = "Off"
234             extra = "force"
235         else:
236             powerstate = "On"
237
238         cmd = "power%s server %s" % (powerstate, bladelist)
239
240         if extra != "":
241             cmd += " %s" % extra
242
243         LOG.debug("Send command to OA: %s" % cmd)
244
245         try:
246             oa.send_command(cmd)
247         except:
248             self.oa_error_message = oa.error_message
249             oa.close()
250             return False
251
252         # Check that all blades reach the state which can take some time,
253         # so re-try a couple of times
254         LOG.debug("Check if state %s successfully set" % state)
255         recheck = 2
256         while True:
257             LOG.debug("Send command to OA: %s" % check)
258             try:
259                 output = oa.send_command(check)
260             except:
261                 self.oa_error_message = oa.error_message
262                 oa.close()
263                 return False
264             for blade in blades:
265                 match = (r"\s+" + str(blade) +
266                          r"\s+\w+\s+\w+.\w+.\w+.\w+\s+\w+\s+%s" %
267                          powerstate)
268                 prog = re.compile(match, re.MULTILINE)
269                 if prog.search(str(output[:])) is None:
270                     recheck -= 1
271                     if recheck >= 0:
272                         # Re-try
273                         time.sleep(3)
274                         break
275                     oa.close()
276                     self.oa_error_message = (
277                         "Could not set state %s on blade %d in shelf %d\n"
278                         % (state, one_blade, shelf))
279                     for line in output:
280                         self.oa_error_message += line
281                     return False
282             else:
283                 # state reached for all blades, exit the infinite loop
284                 break
285
286         if one_blade:
287             LOG.debug("State %s successfully set on blade %d in shelf %d"
288                       % (state, one_blade, shelf))
289         else:
290             LOG.debug("State %s successfully set on blades %s in shelf %d"
291                       % (state, blade_list, shelf))
292         oa.close()
293         return True
294
295     # Change boot order on all blades in shelf
296     # Return None to indicate that no connection do OA succeeded,
297     # Return False to indicate some connection to OA succeeded,
298     # or config error,
299     # Return True to indicate that boot order succesfully changed
300     def set_boot_order(self, shelf, one_blade=None, blade_list=None):
301
302         if one_blade:
303             LOG.debug("Entering: set_bootorder_hp(%d,%d)" % (shelf, one_blade))
304         else:
305             LOG.debug("Entering: set_bootorder_hp(%d)" % shelf)
306
307         self.oa_error_message = ''
308
309         oa = RunOACommand(self.mgmt_ip, self.username, self.password)
310
311         LOG.debug("Connect to active OA for shelf %d" % shelf)
312
313         try:
314             res = oa.connect_to_active()
315         except:
316             self.oa_error_message = oa.error_message
317             return None
318         if res is None:
319             self.oa_error_message = oa.error_message
320             return None
321         if not oa.connected():
322             self.oa_error_message = oa.error_message
323             return False
324
325         if one_blade:
326             blades = [one_blade]
327         else:
328             blades = sorted(blade_list)
329
330         LOG.debug("Check if blades are present")
331
332         check = "show server list"
333
334         LOG.debug("Send command to OA: %s" % check)
335
336         output = oa.send_command(check)
337         first = True
338         bladelist = ''
339         for blade in blades:
340             prog = re.compile(r"\s+" + str(blade) + r"\s+\[Absent\]",
341                               re.MULTILINE)
342             if prog.search(str(output[:])) is not None:
343                 oa.close()
344                 self.oa_error_message = ("Blade %d in shelf %d "
345                                          % (blade, shelf))
346                 if one_blade:
347                     self.oa_error_message += (
348                         "does not exist.\nChange boot order not performed.\n")
349                 else:
350                     self.oa_error_message += (
351                         "specified but does not exist.\n"
352                         "Change boot order not performed on shelf %d\n"
353                         % shelf)
354                 return False
355             if not first:
356                 bladelist += ','
357             else:
358                 first = False
359             bladelist += str(blade)
360
361         if blade_list:
362             LOG.debug("All blades present")
363
364         # Boot origins are pushed so first set boot from hard disk, then PXE
365         # NB! If we want to support boot from SD we must add USB to the "stack"
366         cmd1 = "set server boot first hdd %s" % bladelist
367         cmd2 = "set server boot first pxe %s" % bladelist
368         for cmd in [cmd1, cmd2]:
369
370             LOG.debug("Send command to OA: %s" % cmd)
371             try:
372                 output = oa.send_command(cmd)
373             except:
374                 self.oa_error_message = oa.error_message
375                 for line in output:
376                     self.oa_error_message += line
377                 oa.close()
378                 return False
379
380         # Check that all blades got the correct boot order
381         # Needs updating if USB is added
382         LOG.debug("Check if boot order successfully set")
383         match = (r"^.*Boot Order\):\',\s*\'(\\t)+PXE NIC 1\',\s*\'(\\t)"
384                  r"+Hard Drive")
385         prog = re.compile(match)
386         for blade in blades:
387
388             check = "show server boot %d" % blade
389
390             LOG.debug("Send command to OA: %s" % check)
391             try:
392                 output = oa.send_command(check)
393             except:
394                 self.oa_error_message = oa.error_message
395                 oa.close()
396                 return False
397             if prog.search(str(output[:])) is None:
398                 oa.close()
399                 self.oa_error_message = ("Failed to set boot order on blade "
400                                          "%d in shelf %d\n" % (blade, shelf))
401                 for line in output:
402                     self.oa_error_message += line
403                 return False
404             LOG.debug("Boot order successfully set on blade %d in shelf %d"
405                       % (blade, shelf))
406
407         if blade_list:
408             LOG.debug("Boot order successfully set on all configured blades "
409                       "in shelf %d" % (shelf))
410         oa.close()
411         return True