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