1# (c) 2011-2021 Dennis Kaarsemaker <dennis@kaarsemaker.net> 2# see COPYING for license details 3 4__version__ = "4.4.3" 5 6import codecs 7import io 8import os 9import errno 10import platform 11import random 12import re 13import socket 14import ssl 15import subprocess 16import sys 17import types 18import xml.etree.ElementTree as etree 19import warnings 20import hpilo_fw 21 22PY3 = sys.version_info[0] >= 3 23if PY3: 24 import urllib.request as urllib2 25 class Bogus(Exception): pass 26 socket.sslerror = Bogus 27 basestring = str 28 from os import fsencode 29else: 30 import urllib2 31 fsencode = lambda x: x 32 33# Python 2.7.13 renamed PROTOCOL_SSLv23 to PROTOCOL_TLS 34if not hasattr(ssl, 'PROTOCOL_TLS'): 35 ssl.PROTOCOL_TLS = ssl.PROTOCOL_SSLv23 36 37# Oh the joys of monkeypatching... 38# - We need a CDATA element in set_security_msg, but ElementTree doesn't support it 39# - We need to disable escaping of the PASSWORD attribute, because iLO doesn't 40# unescape it properly 41def CDATA(text=None): 42 element = etree.Element('![CDATA[') 43 element.text = text 44 return element 45 46# Adding this tag to RIBCL scripts should make this hack unnecessary in newer 47# iLO firmware versions. TODO: Check compatibility. 48# <?ilo entity-processing="standard"?> 49class DoNotEscapeMe(str): 50 pass 51 52etree._original_escape_attrib = etree._escape_attrib 53def _escape_attrib(text, *args, **kwargs): 54 if isinstance(text, DoNotEscapeMe): 55 return str(text) 56 else: 57 return etree._original_escape_attrib(text, *args, **kwargs) 58etree._escape_attrib = _escape_attrib 59 60# Python 2.7 and 3 61if hasattr(etree, '_serialize_xml'): 62 etree._original_serialize_xml = etree._serialize_xml 63 def _serialize_xml(write, elem, *args, **kwargs): 64 if elem.tag == '![CDATA[': 65 write("\n<%s%s]]>\n" % (elem.tag, elem.text)) 66 return 67 return etree._original_serialize_xml(write, elem, *args, **kwargs) 68 etree._serialize_xml = etree._serialize['xml'] = _serialize_xml 69# Python 2.6, and non-stdlib ElementTree 70elif hasattr(etree.ElementTree, '_write'): 71 etree.ElementTree._orig_write = etree.ElementTree._write 72 def _write(self, file, node, encoding, namespaces): 73 if node.tag == '![CDATA[': 74 file.write("\n<![CDATA[%s]]>\n" % node.text.encode(encoding)) 75 else: 76 self._orig_write(file, node, encoding, namespaces) 77 etree.ElementTree._write = _write 78else: 79 raise RuntimeError("Don't know how to monkeypatch XML serializer workarounds. Please report a bug at https://github.com/seveas/python-hpilo") 80 81# We handle non-ascii characters in the returned XML by replacing them with XML 82# character references. This likely results in bogus data, but avoids crashes. 83# The iLO should never do this, but firmware bugs may cause it to do so. 84def iloxml_replace(error): 85 ret = "" 86 for pos in range(error.start, len(error.object)): 87 b = error.object[pos] 88 if not isinstance(b, int): 89 b = ord(b) 90 if b < 128: 91 break 92 ret += u'?' 93 warnings.warn("Invalid ascii data found: %s, replaced with %s" % (repr(error.object[error.start:pos]), ret), IloWarning) 94 return (ret, pos) 95codecs.register_error('iloxml_replace', iloxml_replace) 96 97# Which protocol to use 98ILO_RAW = 1 99ILO_HTTP = 2 100ILO_LOCAL = 3 101 102class IloErrorMeta(type): 103 def __new__(cls, name, parents, attrs): 104 if 'possible_messages' not in attrs: 105 attrs['possible_messages'] = [] 106 if 'possible_codes' not in attrs: 107 attrs['possible_codes'] = [] 108 klass = super(IloErrorMeta, cls).__new__(cls, name, parents, attrs) 109 if name != 'IloError': 110 IloError.known_subclasses.append(klass) 111 return klass 112 113class IloError(Exception): 114 __metaclass__ = IloErrorMeta 115 def __init__(self, message, errorcode=None): 116 if issubclass(IloError, object): 117 super(IloError, self).__init__(message) 118 else: 119 Exception.__init__(self, message) 120 self.errorcode = errorcode 121 known_subclasses = [] 122 123if PY3: 124 # Python 3 ignores __metaclass__ but wants class foo(metaclass=bar) But 125 # that syntax is an error on older python, so recreate IloError properly 126 # the manual way. 127 IloError = IloErrorMeta('IloError', (Exception,), {'known_subclasses': [], '__init__': IloError.__init__}) 128 129class IloCommunicationError(IloError): 130 pass 131 132class IloGeneratingCSR(IloError): 133 possible_messages = ['The iLO subsystem is currently generating a Certificate Signing Request(CSR), run script after 10 minutes or more to receive the CSR.'] 134 possible_codes = [0x0088] 135 136class IloLoginFailed(IloError): 137 possible_messages = ['Login failed', 'Login credentials rejected'] 138 possible_codes = [0x005f] 139 140class IloUserNotFound(IloError): 141 possible_codes = [0x000a] 142 143class IloPermissionError(IloError): 144 possible_codes = [0x0023] 145 146class IloNotARackServer(IloError): 147 possible_codes = [0x002a] 148 149class IloLicenseKeyError(IloError): 150 possible_codes = [0x002e] 151 152class IloFeatureNotSupported(IloError): 153 possible_codes = [0x003c] 154 155class IloNotConfigured(IloError): 156 possible_codes = [0x006d] 157 158class IloWarning(Warning): 159 pass 160 161class IloXMLWarning(Warning): 162 pass 163 164class IloTestWarning(Warning): 165 pass 166 167class Ilo(object): 168 """Represents an iLO/iLO2/iLO3/iLO4/RILOE II management interface on a 169 specific host. A new connection using the specified login, password and 170 timeout will be made for each API call. The library will detect which 171 protocol to use, but you can override this by setting protocol to 172 ILO_RAW or ILO_HTTP. Use ILO_LOCAL to avoid using a network connection 173 and use hponcfg instead. Username and password are ignored for ILO_LOCAL 174 connections. Set delayed to True to make python-hpilo not send requests 175 immediately, but group them together. See :func:`call_delayed`""" 176 177 XML_HEADER = b'<?xml version="1.0"?>\r\n' 178 HTTP_HEADER = b"POST /ribcl HTTP/1.1\r\nHost: localhost\r\nContent-Length: %d\r\nConnection: Close%s\r\n\r\n" 179 HTTP_UPLOAD_HEADER = b"POST /cgi-bin/uploadRibclFiles HTTP/1.1\r\nHost: localhost\r\nConnection: Close\r\nContent-Length: %d\r\nContent-Type: multipart/form-data; boundary=%s\r\n\r\n" 180 BLOCK_SIZE = 64 * 1024 181 182 def __init__(self, hostname, login=None, password=None, timeout=60, port=443, protocol=None, delayed=False, ssl_verify=False, ssl_context=None, ssl_version=None): 183 self.hostname = hostname 184 self.login = login or 'Administrator' 185 self.password = password or 'Password' 186 self.timeout = timeout 187 self.debug = 0 188 self.port = port 189 self.protocol = protocol 190 self.ssl_verify = False 191 self.ssl_context = ssl_context 192 self.cookie = None 193 self.delayed = delayed 194 self._elements = None 195 self._processors = [] 196 self.save_response = None 197 self.read_response = None 198 self.save_request = None 199 self._protect_passwords = os.environ.get('HPILO_DONT_PROTECT_PASSWORDS', None) != 'YesPlease' 200 self.firmware_mirror = None 201 self.hponcfg = "/sbin/hponcfg" 202 hponcfg = 'hponcfg' 203 if platform.system() == 'Windows': 204 self.hponcfg = 'C:\Program Files\HP Lights-Out Configuration Utility\cpqlocfg.exe' 205 hponcfg = 'cpqlocfg.exe' 206 for path in os.environ.get('PATH','').split(os.pathsep): 207 maybe = os.path.join(path, hponcfg) 208 if os.access(maybe, os.X_OK): 209 self.hponcfg = maybe 210 break 211 if self.ssl_verify: 212 if sys.version_info < (2,7,9): 213 raise EnvironmentError("SSL verification only works with python 2.7.9 or newer") 214 if not self.ssl_context: 215 self.ssl_context = ssl.create_default_context() 216 # Sadly, ancient iLO's aren't dead yet, so let's enable sslv3 by default 217 self.ssl_context.options &= ~ssl.OP_NO_SSLv3 218 219 def __str__(self): 220 return "iLO interface of %s" % self.hostname 221 222 def _debug(self, level, message): 223 if message.__class__.__name__ == 'bytes': 224 message = message.decode('ascii') 225 if self.debug >= level: 226 if self._protect_passwords: 227 message = re.sub(r'PASSWORD=".*?"', 'PASSWORD="********"', message) 228 sys.stderr.write(message) 229 if message.startswith('\r'): 230 sys.stderr.flush() 231 else: 232 sys.stderr.write('\n') 233 234 def _request(self, xml, progress=None): 235 """Given an ElementTree.Element, serialize it and do the request. 236 Returns an ElementTree.Element containing the response""" 237 if not self.protocol and not self.read_response: 238 self._detect_protocol() 239 240 # Serialize the XML 241 if hasattr(etree, 'tostringlist'): 242 xml = b"\r\n".join(etree.tostringlist(xml)) + b'\r\n' 243 else: 244 xml = etree.tostring(xml) 245 246 header, data = self._communicate(xml, self.protocol, progress=progress) 247 248 # This thing usually contains multiple XML messages 249 messages = [] 250 while data: 251 pos = data.find('<?xml', 5) 252 if pos == -1: 253 message = self._parse_message(data) 254 data = None 255 else: 256 message = self._parse_message(data[:pos]) 257 data = data[pos:] 258 259 # _parse_message returns None if a message has no useful content 260 if message is not None: 261 messages.append(message) 262 263 if not messages: 264 return header, None 265 elif len(messages) == 1: 266 return header, messages[0] 267 else: 268 return header, messages 269 270 def _detect_protocol(self): 271 # Use hponcfg when 'connecting' to localhost 272 if self.hostname == 'localhost': 273 self.protocol = ILO_LOCAL 274 return 275 # Do a bogus request, using the HTTP protocol. If there is no 276 # header (see special case in communicate(), we should be using the 277 # raw protocol 278 header, data = self._communicate(b'<RIBCL VERSION="2.0"></RIBCL>', ILO_HTTP, save=False) 279 if header: 280 self.protocol = ILO_HTTP 281 else: 282 self.protocol = ILO_RAW 283 284 def _upload_file(self, filename, progress): 285 with open(filename, 'rb') as fd: 286 firmware = fd.read() 287 boundary = b'------hpiLO3t%dz' % random.randint(100000,1000000) 288 while boundary in firmware: 289 boundary = b'------hpiLO3t%dz' % str(random.randint(100000,1000000)) 290 parts = [ 291 b"""--%s\r\nContent-Disposition: form-data; name="fileType"\r\n\r\n""" % boundary, 292 b"""\r\n--%s\r\nContent-Disposition: form-data; name="fwimgfile"; filename="%s"\r\nContent-Type: application/octet-stream\r\n\r\n""" % (boundary, fsencode(filename)), 293 firmware, 294 b"\r\n--%s--\r\n" % boundary, 295 ] 296 total_bytes = sum([len(x) for x in parts]) 297 sock = self._get_socket() 298 299 self._debug(2, self.HTTP_UPLOAD_HEADER % (total_bytes, boundary)) 300 sock.write(self.HTTP_UPLOAD_HEADER % (total_bytes, boundary)) 301 for part in parts: 302 if len(part) < self.BLOCK_SIZE: 303 self._debug(2, part) 304 sock.write(part) 305 else: 306 sent = 0 307 fwlen = len(part) 308 while sent < fwlen: 309 written = sock.write(part[sent:sent+self.BLOCK_SIZE]) 310 if written is None: 311 plen = len(part[sent:sent+self.BLOCK_SIZE]) 312 raise IloCommunicationError("Unexpected EOF while sending %d bytes (%d of %d sent before)" % (plen, sent, fwlen)) 313 314 sent += written 315 if callable(progress): 316 progress("Sending request %d/%d bytes (%d%%)" % (sent, fwlen, 100.0*sent/fwlen)) 317 318 data = '' 319 try: 320 while True: 321 d = sock.read() 322 data += d.decode('ascii') 323 if not d: 324 break 325 except socket.sslerror as exc: # Connection closed 326 if not data: 327 raise IloCommunicationError("Communication with %s:%d failed: %s" % (self.hostname, self.port, str(exc))) 328 329 self._debug(1, "Received %d bytes" % len(data)) 330 self._debug(2, data) 331 if 'Set-Cookie:' not in data: 332 # Seen on ilo3 with corrupt filesystem 333 body = re.search('<body>(.*)</body>', data, flags=re.DOTALL).group(1) 334 body = re.sub('<[^>]*>', '', body).strip() 335 body = re.sub('Return to last page', '', body).strip() 336 body = re.sub('\s+', ' ', body).strip() 337 raise IloError(body) 338 self.cookie = re.search('Set-Cookie: *(.*)', data).group(1) 339 self._debug(2, "Cookie: %s" % self.cookie) 340 341 def _get_socket(self): 342 """Set up a subprocess or an https connection and do an HTTP/raw socket request""" 343 if self.read_response or self.save_request: 344 class FakeSocket(object): 345 def __init__(self, rfile=None, wfile=None): 346 self.input = rfile and open(rfile, 'rb') or io.BytesIO() 347 self.output = wfile and open(wfile, 'ab') or io.BytesIO() 348 self.read = self.input.read 349 self.write = self.output.write 350 data = self.input.read(4) 351 self.input.seek(0) 352 self.protocol = data == b'HTTP' and ILO_HTTP or ILO_RAW 353 def close(self): 354 self.input.close() 355 self.output.close() 356 shutdown = lambda *args: None 357 sock = FakeSocket(self.read_response, self.save_request) 358 if self.read_response: 359 self.protocol = sock.protocol 360 return sock 361 362 if self.protocol == ILO_LOCAL: 363 self._debug(1, "Launching hponcfg") 364 try: 365 sp = subprocess.Popen([self.hponcfg, '--input', '--xmlverbose'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 366 except OSError as exc: 367 raise IloCommunicationError("Cannot run %s: %s" % (self.hponcfg, str(exc))) 368 sp.write = sp.stdin.write 369 sp.read = sp.stdout.read 370 return sp 371 372 self._debug(1, "Connecting to %s port %d" % (self.hostname, self.port)) 373 err = None 374 for res in socket.getaddrinfo(self.hostname, self.port, 0, socket.SOCK_STREAM): 375 af, socktype, proto, canonname, sa = res 376 sock = None 377 try: 378 sock = socket.socket(af, socktype, proto) 379 sock.settimeout(self.timeout) 380 self._debug(2, "Connecting to %s port %d" % sa[:2]) 381 sock.connect(sa) 382 except socket.timeout: 383 if sock is not None: 384 sock.close() 385 err = IloCommunicationError("Timeout connecting to %s port %d" % (self.hostname, self.port)) 386 except socket.error as exc: 387 if sock is not None: 388 sock.close() 389 err = IloCommunicationError("Error connecting to %s port %d: %s" % (self.hostname, self.port, str(exc))) 390 391 if err is not None: 392 raise err 393 394 if not sock: 395 raise IloCommunicationError("Unable to resolve %s" % self.hostname) 396 397 try: 398 if not self.ssl_context: 399 self.ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS) 400 # Even more sadly, some iLOs are still using RC4-SHA 401 # which was dropped from the default cipher suite in 402 # Python 2.7.10 and Python 3.4.4. Add it back here :( 403 self.ssl_context.set_ciphers(ssl._DEFAULT_CIPHERS + ":RC4-SHA") 404 return self.ssl_context.wrap_socket( 405 sock, server_hostname=self.hostname) 406 except ssl.SSLError as exc: 407 raise IloCommunicationError("Cannot establish ssl session with %s:%d: %s" % (self.hostname, self.port, str(exc))) 408 409 def _communicate(self, xml, protocol, progress=None, save=True): 410 sock = self._get_socket() 411 if self.read_response: 412 protocol = sock.protocol 413 msglen = len(self.XML_HEADER + xml) 414 if protocol == ILO_HTTP: 415 extra_header = b'' 416 if self.cookie: 417 extra_header = b"\r\nCookie: %s" % self.cookie.encode('ascii') 418 http_header = self.HTTP_HEADER % (msglen, extra_header) 419 msglen += len(http_header) 420 self._debug(1, "Sending XML request, %d bytes" % msglen) 421 422 if protocol == ILO_HTTP: 423 self._debug(2, http_header) 424 sock.write(http_header) 425 426 self._debug(2, self.XML_HEADER + xml) 427 428 # XML header and data need to arrive in 2 distinct packets 429 if self.protocol != ILO_LOCAL: 430 sock.write(self.XML_HEADER) 431 if b'$EMBED' in xml: 432 pre, name, post = re.compile(b'(.*)\$EMBED:(.*)\$(.*)', re.DOTALL).match(xml).groups() 433 sock.write(pre) 434 sent = 0 435 fwlen = os.path.getsize(name) 436 with open(name, 'rb') as fd: 437 fw = fd.read() 438 while sent < fwlen: 439 written = sock.write(fw[sent:sent+self.BLOCK_SIZE]) 440 sent += written 441 if callable(progress): 442 progress("Sending request %d/%d bytes (%d%%)" % (sent, fwlen, 100.0*sent/fwlen)) 443 sock.write(post.strip()) 444 else: 445 sock.write(xml) 446 447 # And grab the data 448 if self.save_request and save: 449 sock.close() 450 return None, None 451 if self.protocol == ILO_LOCAL: 452 # hponcfg doesn't return data until stdin is closed 453 sock.stdin.close() 454 data = '' 455 try: 456 while True: 457 d = sock.read().decode('ascii', 'iloxml_replace') 458 data += d 459 if not d: 460 break 461 if callable(progress) and d.strip().endswith('</RIBCL>'): 462 d = d[d.find('<?xml'):] 463 while '<?xml' in d: 464 end = d.find('<?xml', 5) 465 if end == -1: 466 msg = self._parse_message(d, include_inform=True) 467 if msg: 468 progress(msg) 469 break 470 else: 471 msg = self._parse_message(d[:end], include_inform=True) 472 if msg: 473 progress(msg) 474 d = d[end:] 475 except socket.sslerror as exc: # Connection closed 476 if not data: 477 raise IloCommunicationError("Communication with %s:%d failed: %s" % (self.hostname, self.port, str(exc))) 478 479 self._debug(1, "Received %d bytes" % len(data)) 480 if self.protocol == ILO_LOCAL: 481 sock.stdout.close() 482 sock.wait() 483 elif sock.shutdown: 484 # On OSX this may cause an ENOTCONN, Linux/Windows ignore that situation 485 try: 486 sock.shutdown(socket.SHUT_RDWR) 487 except socket.error as exc: 488 if exc.errno == errno.ENOTCONN: 489 pass 490 else: 491 raise 492 sock.close() 493 494 # Stript out garbage from hponcfg 495 if self.protocol == ILO_LOCAL: 496 data = data[data.find('<'):data.rfind('>')+1] 497 498 if self.save_response and save: 499 with open(self.save_response, 'a') as fd: 500 fd.write(data) 501 502 # Do we have HTTP? 503 header_ = '' 504 if protocol == ILO_HTTP and data.startswith('HTTP/1.1 200'): 505 header, data = data.split('\r\n\r\n', 1) 506 header_ = header 507 header = [x.split(':', 1) for x in header.split('\r\n')[1:]] 508 header = dict([(x[0].lower(), x[1].strip()) for x in header]) 509 if header['transfer-encoding'] == 'chunked': 510 _data, data = data, '' 511 while _data: 512 clen, _data = _data.split('\r\n', 1) 513 clen = int(clen, 16) 514 if clen == 0: 515 break 516 data += _data[:clen] 517 _data = _data[clen+2:] 518 519 elif data.startswith('HTTP/1.1 404'): 520 # We must be using iLO2 or older, they don't do HTTP for XML requests 521 # This case is only triggered by the protocol detection 522 header = None 523 524 elif not data.startswith('<?xml'): 525 if protocol == ILO_LOCAL: 526 raise IloError(sock.stderr.read().strip()) 527 raise IloError("Remote returned bogus data, maybe it's not an iLO") 528 529 else: 530 header = None 531 532 self._debug(2, "%s\r\n\r\n%s" % (header_, data)) 533 return header, data 534 535 536 def _root_element(self, element, **attrs): 537 """Create a basic XML structure for a message. Return root and innermost element""" 538 if not self.delayed or not self._elements: 539 root = etree.Element('RIBCL', VERSION="2.0") 540 login = etree.SubElement(root, 'LOGIN', USER_LOGIN=self.login, PASSWORD=DoNotEscapeMe(self.password)) 541 if self.delayed: 542 if self._elements: 543 root, login = self._elements 544 else: 545 self._elements = (root, login) 546 if self.delayed and len(login) and login[-1].tag == element and login[-1].attrib == attrs: 547 element = login[-1] 548 else: 549 element = etree.SubElement(login, element, **attrs) 550 return root, element 551 552 def _attempt_to_fix_broken_xml(self, data): 553 """ Many iLO versions have bugs that causes them to emit malformed XML. 554 This is a collection of workarounds and kludges to try and fix up the 555 data so ElementTree has a chance to parse it correctly""" 556 557 warnings.warn("iLO returned malformed XML, attempting to fix. Please contact HP to report a bug", IloXMLWarning) 558 559 if '<RIBCL VERSION="2.22"/>' in data: 560 data = data.replace('<RIBCL VERSION="2.22"/>', '<RIBCL VERSION="2.22">') 561 562 # Remove binary 01 in xml output. This bug was seen on a faulty PSU. 563 if '\x01' in data: 564 data = data.replace('\x01', '') 565 566 # Quite a few unescaped quotation mark bugs keep appearing. Let's try 567 # to fix up the XML by replacing the last occurence of a quotation mark 568 # *before* the position of the error. 569 # 570 # Definitely not an optimal algorithm, but this is not a hot path. 571 # Let's favour correctness over hacks. 572 last_position = None 573 position = (0, 0) 574 while position != last_position: 575 last_position = position 576 try: 577 return etree.fromstring(data) 578 except etree.ParseError as e: 579 position = e.position 580 x = position[0]-1 581 y = position[1] 582 lines = data.splitlines() 583 y = lines[x].rfind('"', 0, y) 584 lines[x] = lines[x][:y] + '"' + lines[x][y+1:] 585 data = '\n'.join(lines) 586 # Couldn't fix it :( 587 raise 588 589 def _parse_message(self, data, include_inform=False): 590 """Parse iLO responses into Element instances and remove useless messages""" 591 data = data.strip() 592 if not data: 593 return None 594 try: 595 message = etree.fromstring(data) 596 except etree.ParseError: 597 message = self._attempt_to_fix_broken_xml(data) 598 if message.tag == 'RIBCL': 599 for child in message: 600 if child.tag == 'INFORM': 601 if include_inform: 602 # Filter useless message: 603 if 'should be updated' in child.text: 604 return None 605 return child.text 606 # RESPONSE with status 0 also adds no value 607 # Maybe start adding <?xmlilo output-format="xml"?> to requests. TODO: check compatibility 608 elif child.tag == 'RESPONSE' and int(child.get('STATUS'), 16) == 0: 609 if child.get('MESSAGE') != 'No error': 610 warnings.warn(child.get('MESSAGE'), IloWarning) 611 # These are interesting, something went wrong 612 elif child.tag == 'RESPONSE': 613 if 'syntax error' in child.get('MESSAGE') and not self.protocol: 614 # This is triggered when doing protocol detection, ignore 615 pass 616 else: 617 status = int(child.get('STATUS'), 16) 618 message = child.get('MESSAGE') 619 if 'syntax error' in message: 620 message += '. You may have tried to use a feature this iLO version or firmware version does not support.' 621 for subclass in IloError.known_subclasses: 622 if status in subclass.possible_codes or message in subclass.possible_messages: 623 raise subclass(message, status) 624 raise IloError(message, status) 625 # And this type of message is the actual payload. 626 else: 627 return message 628 return None 629 # This shouldn't be reached as all messages are RIBCL messages. But who knows! 630 return message 631 632 def _element_children_to_dict(self, element): 633 """Returns a dict with tag names of all child elements as keys and the 634 VALUE attributes as values""" 635 retval = {} 636 keys = [elt.tag.lower() for elt in element] 637 if len(keys) != 1 and len(set(keys)) == 1: 638 # Can't return a dict 639 retval = [] 640 for elt in element: 641 # There are some special tags 642 fname = '_parse_%s_%s' % (element.tag.lower(), elt.tag.lower()) 643 if hasattr(self, fname): 644 retval.update(getattr(self, fname)(elt)) 645 continue 646 key, val, unit, description = elt.tag.lower(), elt.get('VALUE', elt.get('value', None)), elt.get('UNIT', None), elt.get('DESCRIPTION', None) 647 if val is None: 648 # HP is not best friends with consistency. Sometimes there are 649 # attributes, sometimes child tags and sometimes text nodes. Oh 650 # well, deal with it :) 651 if element.tag.lower() == 'rimp' or elt.tag.lower() in self.xmldata_ectd.get(element.tag.lower(), []) or elt.tag.lower() == 'temps': 652 val = self._element_children_to_dict(elt) 653 elif elt.attrib and list(elt): 654 val = self._element_to_dict(elt) 655 elif list(elt): 656 val = self._element_to_list(elt) 657 elif elt.text: 658 val = elt.text.strip() 659 elif elt.attrib: 660 val = self._element_to_dict(elt) 661 662 val = self._coerce(val) 663 if unit: 664 val = (val, unit) 665 if description and isinstance(val, str): 666 val = (val, description) 667 if isinstance(retval, list): 668 retval.append(val) 669 elif key in retval: 670 if isinstance(retval[key], dict): 671 retval[key].update(val) 672 elif not isinstance(retval[key], list): 673 retval[key] = [retval[key], val] 674 else: 675 retval[key].append(val) 676 else: 677 retval[key] = val 678 return retval 679 680 def _element_to_dict(self, element): 681 """Returns a dict with tag attributes as items""" 682 retval = {} 683 for key, val in element.attrib.items(): 684 retval[key.lower()] = self._coerce(val) 685 if list(element): 686 fields = [] 687 for child in element: 688 if child.tag == 'FIELD': 689 fields.append(self._element_to_dict(child)) 690 if fields: 691 names = [x['name'] for x in fields] 692 if len(names) == len(set(names)): 693 # Field names are unique, treat them like attributes 694 for field in fields: 695 retval[field['name']] = field['value'] 696 else: 697 # Field names are not unique, such as the name "MAC" 698 retval['fields'] = fields 699 return retval 700 701 def _element_to_list(self, element): 702 tagnames = [x.tag for x in element] 703 if len(set(tagnames)) == 1: 704 return [self._element_children_to_dict(x) for x in element] 705 else: 706 return [(child.tag.lower(), self._element_to_dict(child)) for child in element] 707 708 def _coerce(self, val): 709 """Do some data type coercion: unquote, turn integers into integers and 710 Y/N into booleans""" 711 if isinstance(val, basestring): 712 if val.startswith('"') and val.endswith('"'): 713 val = val[1:-1] 714 if val.isdigit(): 715 val = int(val) 716 else: 717 val = {'Y': True, 'N': False, 'true': True, 'false': False}.get(val, val) 718 return val 719 720 def _raw(self, *tags): 721 if self.delayed: 722 raise IloError("Cannot use raw tags in delayed mode") 723 root, inner = self._root_element(tags[0][0], **(tags[0][1])) 724 for t in tags[1:]: 725 inner = etree.SubElement(inner, t[0], **t[1]) 726 header, message = self._request(root) 727 fd = io.BytesIO() 728 etree.ElementTree(message).write(fd) 729 ret = fd.getvalue() 730 fd.close() 731 return ret 732 733 def _info_tag(self, infotype, tagname, returntags=None, attrib={}, process=lambda x: x): 734 root, inner = self._root_element(infotype, MODE='read') 735 etree.SubElement(inner, tagname, **attrib) 736 if self.delayed: 737 self._processors.append([self._process_info_tag, returntags or [tagname], process]) 738 return 739 header, message = self._request(root) 740 if self.save_request: 741 return 742 return self._process_info_tag(message, returntags or [tagname], process) 743 744 def _process_info_tag(self, message, returntags, process): 745 if isinstance(returntags, basestring): 746 returntags = [returntags] 747 748 for tag in returntags: 749 if message.find(tag) is None: 750 continue 751 message = message.find(tag) 752 if list(message): 753 return process(self._element_children_to_dict(message)) 754 else: 755 return process(self._element_to_dict(message)) 756 raise IloError("Expected tag '%s' not found" % "' or '".join(returntags)) 757 758 def _control_tag(self, controltype, tagname, returntag=None, attrib={}, elements=[], text=None): 759 root, inner = self._root_element(controltype, MODE='write') 760 inner = etree.SubElement(inner, tagname, **attrib) 761 if text: 762 inner.text = text 763 for element in elements: 764 inner.append(element) 765 if self.delayed: 766 if tagname == 'CERTIFICATE_SIGNING_REQUEST': 767 self._processors.append([self._process_control_tag, returntag or tagname]) 768 return 769 header, message = self._request(root) 770 return self._process_control_tag(message, returntag or tagname) 771 772 def _process_control_tag(self, message, returntag): 773 if message is None: 774 return None 775 message = message.find(returntag) 776 if message.text.strip(): 777 return message.text.strip() 778 if not message.attrib and not list(message): 779 return None 780 raise IloError("You've reached unknown territories, please report a bug") 781 if list(message): 782 return self._element_children_to_dict(message) 783 else: 784 return self._element_to_dict(message) 785 786 def call_delayed(self): 787 """In delayed mode, calling a method on an iLO object will not cause an 788 immediate callout to the iLO. Instead, the method and parameters are 789 stored for future calls of this method. This method makes one 790 connection to the iLO and sends all commands as one XML document. 791 This speeds up applications that make many calls to the iLO by 792 removing seconds of overhead per call. 793 794 The return value of call_delayed is a list of return values for 795 individual methods that don't return None. This means that there may 796 be fewer items returned than methods called as only `get_*` methods 797 return data 798 799 Delayed calls only work on iLO 2 or newer""" 800 801 if not self._elements: 802 raise ValueError("No commands scheduled") 803 try: 804 root, inner = self._elements 805 header, message = self._request(root) 806 ret = [] 807 if message is not None: 808 if not isinstance(message, list): 809 message = [message] 810 for message, processor in zip(message, self._processors): 811 ret.append(processor.pop(0)(message, *processor)) 812 finally: 813 self._processors = [] 814 self._elements = None 815 return ret 816 817 def abort_dir_test(self): 818 """Abort authentication directory test""" 819 return self._control_tag('DIR_INFO', 'ABORT_DIR_TEST') 820 821 def activate_license(self, key): 822 """Activate an iLO advanced license""" 823 license = etree.Element('ACTIVATE', KEY=key) 824 return self._control_tag('RIB_INFO', 'LICENSE', elements=[license]) 825 826 def add_federation_group(self, group_name, group_key, admin_priv=False, 827 remote_cons_priv=True, reset_server_priv=False, 828 virtual_media_priv=False, config_ilo_priv=True, login_priv=False): 829 """Add a new federation group""" 830 attrs = locals() 831 elements = [] 832 for attribute in [x for x in attrs.keys() if x.endswith('_priv')]: 833 val = ['No', 'Yes'][bool(attrs[attribute])] 834 elements.append(etree.Element(attribute.upper(), VALUE=val)) 835 836 return self._control_tag('RIB_INFO', 'ADD_FEDERATION_GROUP', elements=elements, 837 attrib={'GROUP_NAME': group_name, 'GROUP_KEY': group_key}) 838 839 def add_sso_server(self, server=None, import_from=None, certificate=None): 840 """Add an SSO server by name (only if SSO trust level is lowered) or by 841 importing a certificate from a server or directly""" 842 if [server, import_from, certificate].count(None) != 2: 843 raise ValueError("You must specify exactly one of server, import_from or certificate") 844 if server: 845 return self._control_tag('SSO_INFO', 'SSO_SERVER', attrib={'NAME': server}) 846 if import_from: 847 return self._control_tag('SSO_INFO', 'SSO_SERVER', attrib={'IMPORT_FROM': import_from}) 848 if certificate: 849 return self._control_tag('SSO_INFO', 'IMPORT_CERTIFICATE', text=certificate) 850 851 def add_user(self, user_login, user_name, password, admin_priv=False, 852 remote_cons_priv=True, reset_server_priv=False, 853 virtual_media_priv=False, config_ilo_priv=True): 854 """Add a new user to the iLO interface with the specified name, 855 password and permissions. Permission attributes should be boolean 856 values.""" 857 attrs = locals() 858 elements = [] 859 for attribute in [x for x in attrs.keys() if x.endswith('_priv')]: 860 val = ['No', 'Yes'][bool(attrs[attribute])] 861 elements.append(etree.Element(attribute.upper(), VALUE=val)) 862 863 return self._control_tag('USER_INFO', 'ADD_USER', elements=elements, 864 attrib={'USER_LOGIN': user_login, 'USER_NAME': user_name, 'PASSWORD': DoNotEscapeMe(password)}) 865 866 def ahs_clear_data(self): 867 """Clears Active Health System information log""" 868 return self._control_tag('RIB_INFO', 'AHS_CLEAR_DATA') 869 870 def cert_fqdn(self, use_fqdn): 871 """Configure whether to use the fqdn or the short hostname for certificate requests""" 872 use_fqdn = str({True: 'Yes', False: 'No'}.get(use_fqdn, use_fqdn)) 873 return self._control_tag('RIB_INFO', 'CERT_FQDN', attrib={'VALUE': use_fqdn}) 874 875 def certificate_signing_request(self, country=None, state=None, locality=None, organization=None, 876 organizational_unit=None, common_name=None): 877 """Get a certificate signing request from the iLO""" 878 vars = locals() 879 del vars['self'] 880 vars = [('CSR_' + x.upper(), vars[x]) for x in vars if vars[x]] 881 elements = map(lambda x: etree.Element(x[0], attrib={'VALUE': str(x[1])}), vars) 882 return self._control_tag('RIB_INFO', 'CERTIFICATE_SIGNING_REQUEST', elements=elements) 883 884 def clear_ilo_event_log(self): 885 """Clears the iLO event log""" 886 return self._control_tag('RIB_INFO', 'CLEAR_EVENTLOG') 887 888 def clear_server_event_log(self): 889 """Clears the server event log""" 890 return self._control_tag('SERVER_INFO', 'CLEAR_IML') 891 892 def clear_server_power_on_time(self): 893 """Clears the server power on time""" 894 return self._control_tag('SERVER_INFO', 'CLEAR_SERVER_POWER_ON_TIME') 895 896 def computer_lock_config(self, computer_lock=None, computer_lock_key=None): 897 """Configure the computer lock settings""" 898 if computer_lock_key: 899 computer_lock = "custom" 900 if not computer_lock: 901 raise ValueError("A value must be specified for computer_lock") 902 elements = [etree.Element('COMPUTER_LOCK', VALUE=computer_lock)] 903 if computer_lock_key: 904 elements.append(etree.Element('COMPUTER_LOCK_KEY', VALUE=computer_lock_key)) 905 return self._control_tag('RIB_INFO', 'COMPUTER_LOCK_CONFIG', elements=elements) 906 907 def dc_registration_complete(self): 908 """Complete the ERS registration of your device after calling 909 set_ers_direct_connect""" 910 return self._control_tag('RIB_INFO', 'DC_REGISTRATION_COMPLETE') 911 912 def delete_federation_group(self, group_name): 913 """Delete the specified federation group membership""" 914 return self._control_tag('RIB_INFO', 'DELETE_FEDERATION_GROUP', attrib={'GROUP_NAME': group_name}) 915 916 def delete_sso_server(self, index): 917 """Delete an SSO server by index""" 918 return self._control_tag('SSO_INFO', 'DELETE_SERVER', 919 attrib={'INDEX': str(index)}) 920 921 def delete_user(self, user_login): 922 """Delete the specified user from the ilo""" 923 return self._control_tag('USER_INFO', 'DELETE_USER', attrib={'USER_LOGIN': user_login}) 924 925 def deactivate_license(self): 926 """Delete the license key from the iLO""" 927 element = etree.Element('DEACTIVATE') 928 return self._control_tag('RIB_INFO', 'LICENSE', elements=[element]) 929 930 def disable_ers(self): 931 """Disable Insight Remote Support functionality and unregister the server""" 932 return self._control_tag('RIB_INFO', 'DISABLE_ERS') 933 934 def eject_virtual_floppy(self): 935 """Eject the virtual floppy""" 936 return self._control_tag('RIB_INFO', 'EJECT_VIRTUAL_FLOPPY') 937 938 def eject_virtual_media(self, device="cdrom"): 939 """Eject the virtual media attached to the specified device""" 940 return self._control_tag('RIB_INFO', 'EJECT_VIRTUAL_MEDIA', 941 attrib={"DEVICE": device.upper()}) 942 943 def ers_ahs_submit(self, message_id, bb_days): 944 """Submity AHS data to the insight remote support server""" 945 elements = [ 946 etree.Element('MESSAGE_ID', attrib={'VALUE': str(message_id)}), 947 etree.Element('BB_DAYS', attrib={'VALUE': str(bb_days)}), 948 ] 949 return self._control_tag('RIB_INFO', 'TRIGGER_BB_DATA', elements=elements) 950 951 def fips_enable(self): 952 """Enable FIPS standard to enforce AES/3DES encryption, can only be 953 reset with a call to factory_defaults. Resets Administrator password 954 and license key""" 955 return self._control_tag('RIB_INFO', 'FIPS_ENABLE') 956 957 def factory_defaults(self): 958 """Reset the iLO to factory default settings""" 959 return self._control_tag('RIB_INFO', 'FACTORY_DEFAULTS') 960 961 def force_format(self): 962 """Forcefully format the iLO's internal NAND flash. Only use this when 963 the iLO is having severe problems and its self-test fails""" 964 return self._control_tag('RIB_INFO', 'FORCE_FORMAT', attrib={'VALUE': 'all'}) 965 966 def get_ahs_status(self): 967 """Get active health system logging status""" 968 return self._info_tag('RIB_INFO', 'GET_AHS_STATUS') 969 970 def get_all_users(self): 971 """Get a list of all loginnames""" 972 def process(data): 973 if isinstance(data, dict): 974 data = data.values() 975 return [x for x in data if x] 976 977 return self._info_tag('USER_INFO', 'GET_ALL_USERS', process=process) 978 979 def get_all_user_info(self): 980 """Get basic and authorization info of all users""" 981 def process(data): 982 if isinstance(data, dict): 983 data = data.values() 984 return dict([(x['user_login'], x) for x in data]) 985 return self._info_tag('USER_INFO', 'GET_ALL_USER_INFO', process=process) 986 987 def get_asset_tag(self): 988 """Gets the server asset tag""" 989 # The absence of an asset tag is communicated in a warning and there 990 # will be *NO* returntag, hence the AttributeError. 991 try: 992 return self._info_tag('SERVER_INFO', 'GET_ASSET_TAG') 993 except AttributeError: 994 return {'asset_tag': None} 995 996 def get_cert_subject_info(self): 997 """Get ssl certificate subject information""" 998 return self._info_tag('RIB_INFO', 'GET_CERT_SUBJECT_INFO', 'CSR_CERT_SETTINGS') 999 1000 def get_critical_temp_remain_off(self): 1001 """Get whether the server will remain powered off after a critical temperature shutdown""" 1002 return self._info_tag('SERVER_INFO', 'GET_CRITICAL_TEMP_REMAIN_OFF') 1003 1004 def get_current_boot_mode(self): 1005 """Get the current boot mode (legaci or uefi)""" 1006 return self._info_tag('SERVER_INFO', 'GET_CURRENT_BOOT_MODE', process=lambda data: data['boot_mode']) 1007 1008 def get_diagport_settings(self): 1009 """Get the blade diagport settings""" 1010 return self._info_tag('RACK_INFO', 'GET_DIAGPORT_SETTINGS') 1011 1012 def get_dir_config(self): 1013 """Get directory authentication configuration""" 1014 return self._info_tag('DIR_INFO', 'GET_DIR_CONFIG') 1015 1016 def get_dir_test_results(self): 1017 """Get the results of the authentication directory test""" 1018 def process(data): 1019 for item in data: 1020 data[item] = dict([(x[0], x[1]['value']) for x in data[item]]) 1021 return data 1022 return self._info_tag('DIR_INFO', 'GET_DIR_TEST_RESULTS', process=process) 1023 1024 def get_embedded_health(self): 1025 """Get server health information""" 1026 def process(data): 1027 for category in data: 1028 if category == 'health_at_a_glance': 1029 health = {} 1030 for key, val in data[category]: 1031 if key not in health: 1032 health[key] = val 1033 else: 1034 health[key].update(val) 1035 data[category] = health 1036 continue 1037 elif isinstance(data[category], list) and data[category]: 1038 for tag in ('label', 'location'): 1039 if tag in data[category][0]: 1040 data[category] = dict([(x[tag], x) for x in data[category]]) 1041 break 1042 elif data[category] in ['', []]: 1043 data[category] = None 1044 return data 1045 return self._info_tag('SERVER_INFO', 'GET_EMBEDDED_HEALTH', 'GET_EMBEDDED_HEALTH_DATA', 1046 process=process) 1047 1048 # Ok, special XML structures. Yay. 1049 def _parse_get_embedded_health_data_drives(self, element): 1050 ret = [] 1051 for bp in element: 1052 if bp.tag != 'BACKPLANE': 1053 raise IloError("Unexpected data returned: %s" % bp.tag) 1054 backplane = obj = {'drive_bays': {}} 1055 ret.append(backplane) 1056 for elt in bp: 1057 if elt.tag == 'DRIVE_BAY': 1058 obj = {} 1059 backplane['drive_bays'][int(elt.get('VALUE'))] = obj 1060 else: 1061 obj[elt.tag.lower()] = elt.get('VALUE') 1062 return {'drives_backplanes': ret} 1063 1064 def _parse_get_embedded_health_data_memory(self, element): 1065 ret = {} 1066 for elt in element: 1067 fname = '_parse_%s_%s' % (element.tag.lower(), elt.tag.lower()) 1068 if hasattr(self, fname): 1069 ret.update(getattr(self, fname)(elt)) 1070 continue 1071 ret[elt.tag.lower()] = self._element_children_to_dict(elt) 1072 return {element.tag.lower(): ret} 1073 _parse_memory_memory_details_summary = _parse_get_embedded_health_data_memory 1074 1075 def _parse_memory_memory_details(self, element): 1076 ret = {} 1077 for elt in element: 1078 if elt.tag not in ret: 1079 ret[elt.tag] = {} 1080 data = self._element_children_to_dict(elt) 1081 ret[elt.tag]["socket %d" % data["socket"]] = data 1082 return {element.tag.lower(): ret} 1083 1084 def _parse_get_embedded_health_data_nic_information(self, element): 1085 return {'nic_information': [self._element_children_to_dict(elt) for elt in element]} 1086 # Can you notice the misspelling?Yes, this is an actual bug in the HP firmware, seen in at least ilo3 1.70 1087 _parse_get_embedded_health_data_nic_infomation = _parse_get_embedded_health_data_nic_information 1088 1089 def _parse_get_embedded_health_data_firmware_information(self, element): 1090 ret = {} 1091 for elt in element: 1092 data = self._element_children_to_dict(elt) 1093 ret[data['firmware_name']] = data['firmware_version'] 1094 return {element.tag.lower(): ret} 1095 1096 def _parse_get_embedded_health_data_storage(self, element): 1097 key = element.tag.lower() 1098 ret = {key: []} 1099 for ctrl in element: 1100 if ctrl.tag == 'DISCOVERY_STATUS': 1101 ret['%s_%s' % (key, ctrl.tag.lower())] = self._element_children_to_dict(ctrl)['status'] 1102 continue 1103 data = {} 1104 for elt in ctrl: 1105 tag = elt.tag.lower() 1106 if tag in ('drive_enclosure', 'logical_drive'): 1107 tag += 's' 1108 if tag not in data: 1109 data[tag] = [] 1110 if tag == 'drive_enclosures': 1111 data[tag].append(self._element_children_to_dict(elt)) 1112 else: 1113 data[tag].append(self._parse_logical_drive(elt)) 1114 else: 1115 data[tag] = elt.get('VALUE') 1116 ret[key].append(data) 1117 return ret 1118 1119 def _parse_logical_drive(self, element): 1120 data = {} 1121 for elt in element: 1122 tag = elt.tag.lower() 1123 if tag == 'physical_drive': 1124 tag += 's' 1125 if tag not in data: 1126 data[tag] = [] 1127 data[tag].append(self._element_children_to_dict(elt)) 1128 else: 1129 data[tag] = elt.get('VALUE') 1130 return data 1131 1132 def _parse_get_embedded_health_data_power_supplies(self, element): 1133 key = element.tag.lower() 1134 ret = {key: {}} 1135 for elt in element: 1136 data = self._element_children_to_dict(elt) 1137 if 'label' in data: 1138 ret[key][data['label']] = data 1139 else: 1140 ret[elt.tag.lower()] = data 1141 return ret 1142 1143 def get_enclosure_ip_settings(self): 1144 """Get the enclosure bay static IP settings""" 1145 return self._info_tag('RACK_INFO', 'GET_ENCLOSURE_IP_SETTINGS') 1146 1147 def get_encrypt_settings(self): 1148 """Get the iLO encryption settings""" 1149 return self._info_tag('RIB_INFO', 'GET_ENCRYPT_SETTINGS') 1150 1151 def get_ers_settings(self): 1152 """Get the ERS Insight Remote Support settings""" 1153 return self._info_tag('RIB_INFO', 'GET_ERS_SETTINGS') 1154 1155 def get_federation_all_groups(self): 1156 """Get all federation group names""" 1157 def process(data): 1158 if isinstance(data, dict): 1159 data = data.values() 1160 return data 1161 return self._info_tag('RIB_INFO', 'GET_FEDERATION_ALL_GROUPS', process=process) 1162 1163 def get_federation_all_groups_info(self): 1164 """Get all federation group names and associated privileges""" 1165 def process(data): 1166 if isinstance(data, dict): 1167 data = data.values() 1168 data = [dict([(key, {'yes': True, 'no': False}.get(val['value'].lower(), val['value'])) for (key, val) in group]) for group in data] 1169 return dict([(x['group_name'], x) for x in data]) 1170 return self._info_tag('RIB_INFO', 'GET_FEDERATION_ALL_GROUPS_INFO', process=process) 1171 1172 def get_federation_group(self, group_name): 1173 """Get privileges for a specific federation group""" 1174 def process(data): 1175 return dict([(key, {'yes': True, 'no': False}.get(val['value'].lower(), val['value'])) for (key, val) in data.values()[0]]) 1176 return self._info_tag('RIB_INFO', 'GET_FEDERATION_GROUP', attrib={'GROUP_NAME': group_name}, process=process) 1177 1178 def get_federation_multicast(self): 1179 """Get the iLO federation mulicast settings""" 1180 return self._info_tag('RIB_INFO', 'GET_FEDERATION_MULTICAST') 1181 1182 def get_fips_status(self): 1183 """Is the FIPS-mandated AES/3DESencryption enforcement in place""" 1184 return self._info_tag('RIB_INFO', 'GET_FIPS_STATUS') 1185 1186 def get_fw_version(self): 1187 """Get the iLO type and firmware version, use get_product_name to get the server model""" 1188 return self._info_tag('RIB_INFO', 'GET_FW_VERSION') 1189 1190 def get_global_settings(self): 1191 """Get global iLO settings""" 1192 return self._info_tag('RIB_INFO', 'GET_GLOBAL_SETTINGS') 1193 1194 def get_host_data(self, decoded_only=True): 1195 """Get SMBIOS records that describe the host. By default only the ones 1196 where human readable information is available are returned. To get 1197 all records pass :attr:`decoded_only=False` """ 1198 1199 def process(data): 1200 if decoded_only: 1201 data = [x for x in data if len(x) > 2] 1202 return data 1203 return self._info_tag('SERVER_INFO', 'GET_HOST_DATA', process=process) 1204 1205 def get_host_power_reg_info(self): 1206 """Get power regulator information""" 1207 return self._control_tag('SERVER_INFO', 'GET_HOST_POWER_REG_INFO') 1208 1209 def get_host_power_saver_status(self): 1210 """Get the configuration of the ProLiant power regulator""" 1211 return self._info_tag('SERVER_INFO', 'GET_HOST_POWER_SAVER_STATUS', 'GET_HOST_POWER_SAVER') 1212 1213 def get_host_power_status(self): 1214 """Whether the server is powered on or not""" 1215 return self._info_tag('SERVER_INFO', 'GET_HOST_POWER_STATUS', 'GET_HOST_POWER', 1216 process=lambda data: data['host_power']) 1217 1218 def get_host_pwr_micro_ver(self): 1219 """Get the version of the power micro firmware""" 1220 return self._info_tag('SERVER_INFO', 'GET_HOST_PWR_MICRO_VER', 1221 process=lambda data: data['pwr_micro']['version']) 1222 1223 def get_ilo_event_log(self): 1224 """Get the full iLO event log""" 1225 def process(data): 1226 if isinstance(data, dict) and 'event' in data: 1227 return [data['event']] 1228 return data 1229 return self._info_tag('RIB_INFO', 'GET_EVENT_LOG', 'EVENT_LOG', process=process) 1230 1231 def get_language(self): 1232 """Get the default language set""" 1233 return self._info_tag('RIB_INFO', 'GET_LANGUAGE') 1234 1235 def get_all_languages(self): 1236 """Get the list of installed languages - broken because iLO returns invalid XML""" 1237 return self._info_tag('RIB_INFO', 'GET_ALL_LANGUAGES') 1238 1239 def get_all_licenses(self): 1240 """Get a list of all license types and licenses""" 1241 def process(data): 1242 if not isinstance(data, list): 1243 data = data.values() 1244 return [dict([(x[0], x[1]['value']) for x in row]) for row in data] 1245 return self._info_tag('RIB_INFO', 'GET_ALL_LICENSES', process=process) 1246 1247 def get_hotkey_config(self): 1248 """Retrieve hotkeys available for use in remote console sessions""" 1249 return self._info_tag('RIB_INFO', 'GET_HOTKEY_CONFIG') 1250 1251 def get_network_settings(self): 1252 """Get the iLO network settings""" 1253 return self._info_tag('RIB_INFO', 'GET_NETWORK_SETTINGS') 1254 1255 def get_oa_info(self): 1256 """Get information about the Onboard Administrator of the enclosing chassis""" 1257 return self._info_tag('BLADESYSTEM_INFO', 'GET_OA_INFO') 1258 1259 def get_one_time_boot(self): 1260 """Get the one time boot state of the host""" 1261 # Inconsistency between iLO 2 and 3, let's fix that 1262 def process(data): 1263 if 'device' in data['boot_type']: 1264 data['boot_type'] = data['boot_type']['device'] 1265 return data['boot_type'].lower() 1266 return self._info_tag('SERVER_INFO', 'GET_ONE_TIME_BOOT', ('ONE_TIME_BOOT', 'GET_ONE_TIME_BOOT'), process=process) 1267 1268 def get_pending_boot_mode(self): 1269 """Get the pending boot mode (legaci or uefi)""" 1270 return self._info_tag('SERVER_INFO', 'GET_PENDING_BOOT_MODE', process=lambda data: data['boot_mode']) 1271 1272 def get_persistent_boot(self): 1273 """Get the boot order of the host. For uEFI hosts (gen9+), this returns 1274 a list of tuples (name, description. For older host it returns a 1275 list of names""" 1276 def process(data): 1277 if isinstance(data, dict): 1278 data = list(data.items()) 1279 data.sort(key=lambda x: x[1]) 1280 return [x[0].lower() for x in data] 1281 elif isinstance(data[0], tuple): 1282 return data 1283 return [x.lower() for x in data] 1284 return self._info_tag('SERVER_INFO', 'GET_PERSISTENT_BOOT', ('PERSISTENT_BOOT', 'GET_PERSISTENT_BOOT'), process=process) 1285 1286 def get_pers_mouse_keyboard_enabled(self): 1287 """Returns whether persistent mouse and keyboard are enabled""" 1288 return self._info_tag('SERVER_INFO', 'GET_PERS_MOUSE_KEYBOARD_ENABLED', process=lambda data: data['persmouse_enabled']) 1289 1290 def get_power_cap(self): 1291 """Get the power cap setting""" 1292 return self._info_tag('SERVER_INFO', 'GET_POWER_CAP', process=lambda data: data['power_cap']) 1293 1294 def get_power_readings(self): 1295 """Get current, min, max and average power readings""" 1296 return self._info_tag('SERVER_INFO', 'GET_POWER_READINGS') 1297 1298 def get_product_name(self): 1299 """Get the model name of the server, use get_fw_version to get the iLO model""" 1300 return self._info_tag('SERVER_INFO', 'GET_PRODUCT_NAME', process=lambda data: data['product_name']) 1301 1302 def get_pwreg(self): 1303 """Get the power and power alert threshold settings""" 1304 return self._info_tag('SERVER_INFO', 'GET_PWREG') 1305 1306 def get_rack_settings(self): 1307 """Get the rack settings for an iLO""" 1308 return self._info_tag('RACK_INFO', 'GET_RACK_SETTINGS') 1309 1310 def get_sdcard_status(self): 1311 """Get whether an SD card is connected to the server""" 1312 return self._info_tag('SERVER_INFO', 'GET_SDCARD_STATUS') 1313 1314 def get_security_msg(self): 1315 """Retrieve the security message that is displayed on the login screen""" 1316 return self._info_tag('RIB_INFO', 'GET_SECURITY_MSG') 1317 1318 def get_server_auto_pwr(self): 1319 """Get the automatic power on delay setting""" 1320 return self._info_tag('SERVER_INFO', 'GET_SERVER_AUTO_PWR', process=lambda data: data['server_auto_pwr']) 1321 1322 def get_server_event_log(self): 1323 """Get the IML log of the server""" 1324 def process(data): 1325 if isinstance(data, dict) and 'event' in data: 1326 return [data['event']] 1327 return data 1328 return self._info_tag('SERVER_INFO', 'GET_EVENT_LOG', 'EVENT_LOG', process=process) 1329 1330 def get_server_fqdn(self): 1331 """Get the fqdn of the server this iLO is managing""" 1332 return self._info_tag('SERVER_INFO', 'GET_SERVER_FQDN', 'SERVER_FQDN', process=lambda fqdn: fqdn['value']) 1333 1334 def get_server_name(self): 1335 """Get the name of the server this iLO is managing""" 1336 return self._info_tag('SERVER_INFO', 'GET_SERVER_NAME', 'SERVER_NAME', process=lambda name: name['value']) 1337 1338 def get_server_power_on_time(self): 1339 """How many minutes ago has the server been powered on""" 1340 return self._info_tag('SERVER_INFO', 'GET_SERVER_POWER_ON_TIME', 'SERVER_POWER_ON_MINUTES', process=lambda data: int(data['value'])) 1341 1342 def get_smh_fqdn(self): 1343 """Get the fqdn of the HP System Management Homepage""" 1344 return self._info_tag('SERVER_INFO', 'GET_SMH_FQDN', 'SMH_FQDN', process=lambda fqdn: fqdn['value']) 1345 1346 def get_snmp_im_settings(self): 1347 """Where does the iLO send SNMP traps to and which traps does it send""" 1348 return self._info_tag('RIB_INFO', 'GET_SNMP_IM_SETTINGS') 1349 1350 def get_spatial(self): 1351 """Get location information""" 1352 return self._info_tag('SERVER_INFO', 'GET_SPATIAL', 'SPATIAL') 1353 1354 def get_sso_settings(self): 1355 """Get the HP SIM Single Sign-On settings""" 1356 return self._info_tag('SSO_INFO', 'GET_SSO_SETTINGS') 1357 1358 def get_supported_boot_mode(self): 1359 return self._info_tag('SERVER_INFO', 'GET_SUPPORTED_BOOT_MODE', process=lambda data: data['supported_boot_mode']) 1360 1361 def get_topology(self): 1362 """Get rack topology information""" 1363 return self._info_tag('RACK_INFO', 'GET_TOPOLOGY') 1364 1365 def get_tpm_status(self): 1366 """Get the status of the Trusted Platform Module""" 1367 return self._info_tag('SERVER_INFO', 'GET_TPM_STATUS') 1368 1369 def get_twofactor_settings(self): 1370 """Get two-factor authentication settings""" 1371 return self._info_tag('RIB_INFO', 'GET_TWOFACTOR_SETTINGS') 1372 1373 def get_uid_status(self): 1374 """Get the status of the UID light""" 1375 return self._info_tag('SERVER_INFO', 'GET_UID_STATUS', process=lambda data: data['uid']) 1376 1377 def get_user(self, user_login): 1378 """Get user info about a specific user""" 1379 return self._info_tag('USER_INFO', 'GET_USER', attrib={'USER_LOGIN': user_login}) 1380 1381 def get_vm_status(self, device="CDROM"): 1382 """Get the status of virtual media devices. Valid devices are FLOPPY and CDROM""" 1383 return self._info_tag('RIB_INFO', 'GET_VM_STATUS', attrib={'DEVICE': device}) 1384 1385 def hotkey_config(self, ctrl_t=None, ctrl_u=None, ctrl_v=None, ctrl_w=None, 1386 ctrl_x=None, ctrl_y=None): 1387 """Change remote console hotkeys""" 1388 vars = locals() 1389 del vars['self'] 1390 elements = [etree.Element(x.upper(), VALUE=vars[x]) for x in vars if vars[x] is not None] 1391 return self._control_tag('RIB_INFO', 'HOTKEY_CONFIG', elements=elements) 1392 1393 def import_certificate(self, certificate): 1394 """Import a signed SSL certificate""" 1395 return self._control_tag('RIB_INFO', 'IMPORT_CERTIFICATE', text=certificate) 1396 1397 # Broken in iLO3 < 1.55 for Administrator 1398 def import_ssh_key(self, user_login, ssh_key): 1399 """Imports an SSH key for the specified user. The value of ssh_key 1400 should be the content of an id_dsa.pub or id_rsa.pub file""" 1401 # Basic sanity checking 1402 if ' ' not in ssh_key: 1403 raise ValueError("Invalid SSH key") 1404 algo, key = ssh_key.split(' ',2)[:2] 1405 if algo not in ['ssh-dss', 'ssh-rsa']: 1406 raise ValueError("Invalid SSH key, only DSA and RSA keys are supported") 1407 try: 1408 key.decode('base64') 1409 except Exception: 1410 raise ValueError("Invalid SSH key") 1411 key_ = "-----BEGIN SSH KEY-----\r\n%s\r\n%s %s\r\n-----END SSH KEY-----\r\n" % (algo, key, user_login) 1412 return self._control_tag('RIB_INFO', 'IMPORT_SSH_KEY', text=key_) 1413 1414 def delete_ssh_key(self, user_login): 1415 """Delete a users SSH key""" 1416 return self._control_tag('USER_INFO', 'MOD_USER', attrib={'USER_LOGIN': user_login}, elements=[etree.Element('DEL_USERS_SSH_KEY')]) 1417 1418 def insert_virtual_media(self, device, image_url): 1419 """Insert a virtual floppy or CDROM. Note that you will also need to 1420 use :func:`set_vm_status` to connect the media""" 1421 return self._control_tag('RIB_INFO', 'INSERT_VIRTUAL_MEDIA', attrib={'DEVICE': device.upper(), 'IMAGE_URL': image_url}) 1422 1423 def mod_encrypt_settings(self, user_login, password, ilo_group_name, cert_name, enable_redundancy, 1424 primary_server_address, primary_server_port, secondary_server_address=None, secondary_server_port=None): 1425 """Configure encryption settings""" 1426 vars = locals() 1427 del vars['self'] 1428 elements = [] 1429 for var in ['ilo_group_name', 'enable_redundancy']: 1430 if vars[var] is not None: 1431 elements.append(etree.Element(var.upper(), VALUE=vars.pop(var))) 1432 for var in vars: 1433 if vars[var] is not None: 1434 elements.append(etree.Element('ESKM_' + var.upper(), VALUE=vars[var])) 1435 return self._control_tag('RIB_INFO', 'MOD_ENCRYPT_SETTINGS', elements=elements) 1436 1437 def mod_federation_group(self, group_name, new_group_name=None, group_key=None, 1438 admin_priv=None, remote_cons_priv=None, reset_server_priv=None, 1439 virtual_media_priv=None, config_ilo_priv=None, login_priv=None): 1440 """Set attributes for a federation group, only specified arguments will 1441 be changed. All arguments except group_name, new_group_name and 1442 group_key should be boolean""" 1443 attrs = locals() 1444 elements = [] 1445 if attrs['new_group_name'] is not None: 1446 elements.append(etree.Element('GROUP_NAME', VALUE=attrs['new_group_name'])) 1447 if attrs['group_key'] is not None: 1448 elements.append(etree.Element('PASSWORD', VALUE=attrs['group_key'])) 1449 for attribute in [x for x in attrs.keys() if x.endswith('_priv')]: 1450 if attrs[attribute] is not None: 1451 val = ['No', 'Yes'][bool(attrs[attribute])] 1452 elements.append(etree.Element(attribute.upper(), VALUE=val)) 1453 return self._control_tag('RIB_INFO', 'MOD_FEDERATION_GROUP', attrib={'GROUP_NAME': group_name}, elements=elements) 1454 1455 def mod_global_settings(self, ilo_funct_enabled=None, rbsu_post_ip=None, 1456 # Access settings 1457 f8_prompt_enabled=None, f8_login_required=None, lock_configuration=None, 1458 serial_cli_status=None, serial_cli_speed=None, 1459 http_port=None, https_port=None, ssh_port=None, ssh_status=None, 1460 ipmi_dcmi_over_lan_enabled=None, ipmi_dcmi_over_lan_port=None, 1461 remote_console_port_status=None, remote_console_port=None, remote_console_encryption=None, 1462 rawvsp_port=None, vsp_software_flow_control=None, 1463 terminal_services_port=None, 1464 shared_console_enable=None, shared_console_port=None, remote_console_acquire=None, 1465 telnet_enable=None, ssl_empty_records_enable=None, remote_console_status=None, 1466 ribcl_status=None, virtual_media_status=None, webgui_status=None, webserver_status=None, 1467 1468 # Security settings 1469 min_password=None, enforce_aes=None, authentication_failure_logging=None, 1470 authentication_failure_delay_secs=None, authentication_failures_before_delay=None, 1471 ssl_v3_enable=None, session_timeout=None, 1472 1473 # Monitoring & alerting 1474 snmp_access_enabled=None, snmp_port=None, snmp_trap_port=None, 1475 remote_syslog_enable=None, remote_syslog_server_address=None, remote_syslog_port=None, 1476 alertmail_enable=None, alertmail_email_address=None, 1477 alertmail_sender_domain=None, alertmail_smtp_server=None, alertmail_smtp_port=None, 1478 alertmail_smtp_auth_enable=None, alertmail_smtp_auth_username=None, 1479 alertmail_smtp_secure_enable=None, 1480 1481 # Console capturing 1482 vsp_log_enable=None, 1483 interactive_console_replay_enable=None, console_capture_enable=None, 1484 console_capture_boot_buffer_enable=None, console_capture_fault_buffer_enable=None, 1485 console_capture_port=None, 1486 capture_auto_export_enable=None, capture_auto_export_location=None, 1487 capture_auto_export_username=None, capture_auto_export_password=None, 1488 1489 # And the rest 1490 remote_keyboard_model=None, virtual_kbmouse_connection=None, vmedia_disable=None, 1491 virtual_media_port=None, key_up_key_down_enable=None, high_performance_mouse=None, 1492 brownout_recovery=None, enhanced_cli_prompt_enable=None, tcp_keep_alive_enable=None, 1493 propagate_time_to_host=None, passthrough_config=None): 1494 """Modify iLO global settings, only values that are specified will be changed. Note that 1495 many settings only work on certain iLO models and firmware versions""" 1496 vars = dict(locals()) 1497 del vars['self'] 1498 1499 # even though a get_global_settings returns the actual speed we have to use 1500 # numerical values between 0 (unchanged) and 6 to represent speed 1501 serial_cli_speed_options = { 1502 '9600': 1, 1503 '19200': 2, 1504 '38400': 3, 1505 '57600': 4, 1506 '115200': 5, 1507 } 1508 1509 # same with serial_cli_status 1510 serial_cli_status_options = { 1511 'Disabled': 1, 1512 'Enabled-No Authentication': 2, 1513 'Enabled-Authentication Required': 3, 1514 } 1515 1516 # and authentication_failure_logging 1517 authentication_failure_logging_options = { 1518 'Disabled': 0, 1519 'Enabled-every failure': 1, 1520 'Enabled-every 2nd failure': 2, 1521 'Enabled-every 3rd failure': 3, 1522 'Enabled-every 5th failure': 5, 1523 } 1524 1525 vars_mappings = { 1526 "serial_cli_speed": serial_cli_speed_options, 1527 "serial_cli_status": serial_cli_status_options, 1528 "authentication_failure_logging": authentication_failure_logging_options 1529 } 1530 1531 for var_name, var_mappings in vars_mappings.items(): 1532 if vars.get(var_name, None) is not None: 1533 var_value = str(vars.get(var_name)) 1534 vars[var_name] = str(var_mappings.get(var_value,var_value)) 1535 1536 dont_map = ['authentication_failure_logging', 'authentication_failures_before_delay', 'serial_cli_speed', 1537 'min_password', 'session_timeout', "serial_cli_status"] 1538 elements = [etree.Element(x.upper(), VALUE=str({True: 'Yes', False: 'No'}.get(vars[x], vars[x]))) 1539 for x in vars if vars[x] is not None and x not in dont_map] + \ 1540 [etree.Element(x.upper(), VALUE=str(vars[x])) 1541 for x in vars if vars[x] is not None and x in dont_map] 1542 return self._control_tag('RIB_INFO', 'MOD_GLOBAL_SETTINGS', elements=elements) 1543 1544 def mod_network_settings(self, enable_nic=None, reg_ddns_server=None, 1545 ping_gateway=None, dhcp_domain_name=None, speed_autoselect=None, 1546 nic_speed=None, full_duplex=None, dhcp_enable=None, 1547 ip_address=None, subnet_mask=None, gateway_ip_address=None, 1548 dns_name=None, domain_name=None, dhcp_gateway=None, 1549 dhcp_dns_server=None, dhcp_wins_server=None, dhcp_static_route=None, 1550 reg_wins_server=None, prim_dns_server=None, sec_dns_server=None, 1551 ter_dns_server=None, prim_wins_server=None, sec_wins_server=None, 1552 static_route_1=None, static_route_2=None, static_route_3=None, 1553 dhcp_sntp_settings=None, sntp_server1=None, sntp_server2=None, 1554 timezone=None, enclosure_ip_enable=None, web_agent_ip_address=None, 1555 shared_network_port=None, vlan_enabled=None, vlan_id=None, 1556 shared_network_port_vlan=None, shared_network_port_vlan_id=None, ipv6_address=None, 1557 ipv6_static_route_1=None, ipv6_static_route_2=None, ipv6_static_route_3=None, 1558 ipv6_prim_dns_server=None, ipv6_sec_dns_server=None, ipv6_ter_dns_server=None, 1559 ipv6_default_gateway=None, ipv6_preferred_protocol=None, ipv6_addr_autocfg=None, 1560 ipv6_reg_ddns_server=None, dhcpv6_dns_server=None, dhcpv6_rapid_commit=None, 1561 dhcpv6_stateful_enable=None, dhcpv6_stateless_enable=None, dhcpv6_sntp_settings=None, 1562 dhcpv6_domain_name=None, ilo_nic_auto_select=None, ilo_nic_auto_snp_scan=None, 1563 ilo_nic_auto_delay=None, ilo_nic_fail_over=None, gratuitous_arp=None, 1564 ilo_nic_fail_over_delay=None, snp_port=None): 1565 """Configure the network settings for the iLO card. The static route arguments require 1566 dicts as arguments. The necessary keys in these dicts are dest, 1567 gateway and mask all in dotted-quad form""" 1568 vars = dict(locals()) 1569 del vars['self'] 1570 1571 # For the ipv4 route elements, {'dest': XXX, 'gateway': XXX} 1572 # ipv6 routes are ipv6_dest, prefixlen, ipv6_gateway 1573 # IPv6 addresses may specify prefixlength as /64 (default 64) 1574 dont_map = ['prefixlen', 'ilo_nic_auto_snp_scan', 'ilo_nic_auto_delay', 'ilo_nic_fail_over_delay', 'snp_port', 'vlan_id'] 1575 elements = [etree.Element(x.upper(), VALUE=str({True: 'Yes', False: 'No'}.get(vars[x], vars[x]))) 1576 for x in vars if vars[x] is not None and 'static_route_' not in x and x not in dont_map] + \ 1577 [etree.Element(x.upper(), VALUE=str(vars[x])) 1578 for x in vars if vars[x] is not None and 'static_route_' not in x and x in dont_map] 1579 1580 for key in vars: 1581 if 'static_route_' not in key or not vars[key]: 1582 continue 1583 val = vars[key] 1584 # Uppercase all keys 1585 for key_ in val.keys(): 1586 val[key_.upper()] = val.pop(key_) 1587 elements.append(etree.Element(key.upper(), **val)) 1588 1589 for element in elements: 1590 if element.tag == 'IPV6_ADDRESS': 1591 addr = element.attrib['VALUE'] 1592 if '/' in addr: 1593 addr, plen = addr.rsplit('/', 1) 1594 element.attrib.update({'VALUE': addr, 'PREFIXLEN': plen}) 1595 if 'PREFIXLEN' not in element.attrib: 1596 element.attrib['PREFIXLEN'] = '64' 1597 if "IPV6_STATIC_ROUTE_" in element.tag: 1598 plen = element.attrib['PREFIXLEN'] 1599 if not isinstance(plen, basestring): 1600 element.attrib['PREFIXLEN'] = str(plen) 1601 return self._control_tag('RIB_INFO', 'MOD_NETWORK_SETTINGS', elements=elements) 1602 mod_network_settings.requires_dict = ['static_route_1', 'static_route_2', 'static_route_3', 1603 'ipv6_static_route_1', 'ipv6_static_route_2', 'ipv6_static_route_3'] 1604 1605 def mod_dir_config(self, dir_authentication_enabled=None, 1606 dir_local_user_acct=None,dir_server_address=None, 1607 dir_server_port=None,dir_object_dn=None,dir_object_password=None, 1608 dir_user_context_1=None,dir_user_context_2=None, 1609 dir_user_context_3=None,dir_user_context_4=None, 1610 dir_user_context_5=None,dir_user_context_6=None, 1611 dir_user_context_7=None,dir_user_context_8=None, 1612 dir_user_context_9=None,dir_user_context_10=None, 1613 dir_user_context_11=None,dir_user_context_12=None, 1614 dir_user_context_13=None,dir_user_context_14=None, 1615 dir_user_context_15=None,dir_enable_grp_acct=None, 1616 dir_kerberos_enabled=None,dir_kerberos_realm=None, 1617 dir_kerberos_kdc_address=None,dir_kerberos_kdc_port=None, 1618 dir_kerberos_keytab=None, 1619 dir_generic_ldap_enabled=None, 1620 dir_grpacct1_name=None,dir_grpacct1_sid=None, 1621 dir_grpacct1_priv=None,dir_grpacct2_name=None, 1622 dir_grpacct2_sid=None,dir_grpacct2_priv=None, 1623 dir_grpacct3_name=None,dir_grpacct3_sid=None, 1624 dir_grpacct3_priv=None,dir_grpacct4_name=None, 1625 dir_grpacct4_sid=None,dir_grpacct4_priv=None, 1626 dir_grpacct5_name=None,dir_grpacct5_sid=None, 1627 dir_grpacct5_priv=None,dir_grpacct6_name=None, 1628 dir_grpacct6_sid=None,dir_grpacct6_priv=None): 1629 """Modify iLO directory configuration, only values that are specified 1630 will be changed.""" 1631 vars = dict(locals()) 1632 del vars['self'] 1633 1634 # The _priv thing is a comma-separated list of numbers, but other 1635 # functions use names, and the iLO ssh interface shows different names. 1636 # Support them all. 1637 privmap = { 1638 'login': 1, 1639 'rc': 2, 1640 'remote_cons': 2, 1641 'vm': 3, 1642 'virtual_media': 3, 1643 'power': 4, 1644 'reset_server': 4, 1645 'config': 5, 1646 'config_ilo': 5, 1647 'admin': 6, 1648 } 1649 1650 # create special case for element with text inside 1651 if dir_kerberos_keytab: 1652 keytab_el = etree.Element('DIR_KERBEROS_KEYTAB') 1653 keytab_el.text = dir_kerberos_keytab 1654 del vars['dir_kerberos_keytab'] 1655 1656 elements = [] 1657 for key, val in vars.items(): 1658 if val is None: 1659 continue 1660 if key.endswith('_priv'): 1661 if isinstance(val, basestring): 1662 val = val.replace('oemhp_', '').replace('_priv', '').split(',') 1663 val = ','.join([str(privmap.get(x,x)) for x in val]) 1664 else: 1665 val = str({True: 'Yes', False: 'No'}.get(val, val)) 1666 elements.append(etree.Element(key.upper(), VALUE=val)) 1667 1668 if dir_kerberos_keytab: 1669 elements.append(keytab_el) 1670 return self._control_tag('DIR_INFO','MOD_DIR_CONFIG',elements=elements) 1671 1672 1673 def mod_snmp_im_settings(self, snmp_access=None, web_agent_ip_address=None, 1674 snmp_address_1=None, snmp_address_1_rocommunity=None, snmp_address_1_trapcommunity=None, 1675 snmp_address_2=None, snmp_address_2_rocommunity=None, snmp_address_2_trapcommunity=None, 1676 snmp_address_3=None, snmp_address_3_rocommunity=None, snmp_address_3_trapcommunity=None, 1677 snmp_port=None, snmp_trap_port=None, snmp_v3_engine_id=None, snmp_passthrough_status=None, 1678 trap_source_identifier=None, os_traps=None, rib_traps=None, cold_start_trap_broadcast=None, 1679 snmp_v1_traps=None, cim_security_mask=None, snmp_sys_location=None, snmp_sys_contact=None, 1680 agentless_management_enable=None, snmp_system_role=None, snmp_system_role_detail=None, 1681 snmp_user_profile_1=None, snmp_user_profile_2=None, snmp_user_profile_3=None): 1682 """Configure the SNMP and Insight Manager integration settings. The 1683 trapcommunity settings must be dicts with keys value (the name of 1684 the community) and version (1 or 2c)""" 1685 vars = dict(locals()) 1686 del vars['self'] 1687 elements = [etree.Element(x.upper(), VALUE=str({True: 'Yes', False: 'No'}.get(vars[x], vars[x]))) 1688 for x in vars if vars[x] is not None and 'trapcommunity' not in x and 'snmp_user_profile' not in x] 1689 for key in vars: 1690 if 'trapcommunity' in key and vars[key]: 1691 val = vars[key] 1692 for key_ in val.keys(): 1693 val[key_.upper()] = str(val.pop(key_)) 1694 elements.append(etree.Element(key.upper(), **val)) 1695 elif 'snmp_user_profile' in key and vars[key]: 1696 elt = etree.Element(key[:-2].upper(), {'INDEX': key[-1]}) 1697 for key, val in vars[key].items(): 1698 etree.SubElement(elt, key.upper(), VALUE=str(val)) 1699 elements.append(elt) 1700 return self._control_tag('RIB_INFO', 'MOD_SNMP_IM_SETTINGS', elements=elements) 1701 mod_snmp_im_settings.requires_dict = ['snmp_user_profile_1', 'snmp_user_profile_2', 'snmp_user_profile_3', 1702 'snmp_address_1_trapcommunity', 'snmp_address_2_trapcommunity', 'snmp_address_3_trapcommunity'] 1703 1704 def mod_sso_settings(self, trust_mode=None, user_remote_cons_priv=None, 1705 user_reset_server_priv=None, user_virtual_media_priv=None, 1706 user_config_ilo_priv=None, user_admin_priv=None, 1707 operator_login_priv=None, operator_remote_cons_priv=None, 1708 operator_reset_server_priv=None, operator_virtual_media_priv=None, 1709 operator_config_ilo_priv=None, operator_admin_priv=None, 1710 administrator_login_priv=None, administrator_remote_cons_priv=None, 1711 administrator_reset_server_priv=None, administrator_virtual_media_priv=None, 1712 administrator_config_ilo_priv=None, administrator_admin_priv=None): 1713 vars = dict(locals()) 1714 del vars['self'] 1715 del vars['trust_mode'] 1716 elements = [] 1717 if trust_mode is not None: 1718 elements.append(etree.Element('TRUST_MODE', attrib={'VALUE': trust_mode})) 1719 vars = [(x.upper().split('_', 1), {True: 'Yes', False: 'No'}.get(vars[x], vars[x])) for x in vars if vars[x]] 1720 elements += [etree.Element(x[0][0] + '_ROLE', attrib={x[0][1]: x[1]}) for x in vars] 1721 return self._control_tag('SSO_INFO', 'MOD_SSO_SETTINGS', elements=elements) 1722 1723 def mod_twofactor_settings(self, auth_twofactor_enable=None, cert_revocation_check=None, cert_owner_san=None, cert_owner_subject=None): 1724 """Modify the twofactor authenticatino settings""" 1725 elements = [] 1726 if auth_twofactor_enable is not None: 1727 elements.append(etree.Element('AUTH_TWOFACTOR_ENABLE', VALUE=['No', 'Yes'][bool(auth_twofactor_enable)])) 1728 if cert_revocation_check is not None: 1729 elements.append(etree.Element('CERT_REVOCATION_CHECK', VALUE=['No', 'Yes'][bool(cert_revocation_check)])) 1730 if cert_owner_san: 1731 elements.append(etree.Element('CERT_OWNER_SAN')) 1732 if cert_owner_subject: 1733 elements.append(etree.Element('CERT_OWNER_SUBJECT')) 1734 return self._control_tag('RIB_INFO', 'MOD_TWOFACTOR_SETTINGS', elements=elements) 1735 1736 1737 def mod_user(self, user_login, user_name=None, password=None, 1738 admin_priv=None, remote_cons_priv=None, reset_server_priv=None, 1739 virtual_media_priv=None, config_ilo_priv=None): 1740 """Set attributes for a user, only specified arguments will be changed. 1741 All arguments except user_name and password should be boolean""" 1742 1743 attrs = locals() 1744 elements = [] 1745 if attrs['user_name'] is not None: 1746 elements.append(etree.Element('USER_NAME', VALUE=attrs['user_name'])) 1747 if attrs['password'] is not None: 1748 elements.append(etree.Element('PASSWORD', VALUE=DoNotEscapeMe(attrs['password']))) 1749 for attribute in [x for x in attrs.keys() if x.endswith('_priv')]: 1750 if attrs[attribute] is not None: 1751 val = ['No', 'Yes'][bool(attrs[attribute])] 1752 elements.append(etree.Element(attribute.upper(), VALUE=val)) 1753 1754 return self._control_tag('USER_INFO', 'MOD_USER', attrib={'USER_LOGIN': user_login}, elements=elements) 1755 1756 def press_pwr_btn(self): 1757 """Press the power button""" 1758 return self._control_tag('SERVER_INFO', 'PRESS_PWR_BTN') 1759 1760 def profile_apply(self, desc_name, action): 1761 """Apply a deployment profile""" 1762 elements = [ 1763 etree.Element('PROFILE_DESC_NAME', attrib={'VALUE': desc_name}), 1764 etree.Element('PROFILE_OPTIONS', attrib={'VALUE': 'none'}), # Currently unused 1765 etree.Element('PROFILE_ACTION', attrib={'VALUE': action}), 1766 ] 1767 return self._control_tag('RIB_INFO', 'PROFILE_APPLY', elements=elements) 1768 1769 def profile_apply_get_results(self): 1770 """Retrieve the results of the last profile_apply""" 1771 return self._info_tag('RIB_INFO', 'PROFILE_APPLY_GET_RESULTS') 1772 1773 def profile_delete(self, desc_name): 1774 """Delet the specified deployment profile""" 1775 return self._control_tag('RIB_INFO', 'PROFILE_DELETE', elements=[etree.Element('PROFILE_DESC_NAME', attrib={'VALUE': desc_name})]) 1776 1777 def profile_desc_download(self, desc_name, name, description, blob_namespace='perm', blob_name=None, url=None): 1778 """Make the iLO download a blob and create a deployment profile""" 1779 elements = [ 1780 etree.Element('PROFILE_DESC_NAME', attrib={'VALUE': desc_name}), 1781 etree.Element('PROFILE_NAME', attrib={'VALUE': name}), 1782 etree.Element('PROFILE_DESCRIPTION', attrib={'VALUE': description}), 1783 etree.Element('PROFILE_SCHEMA', attrib={'VALUE': 'intelligentprovisioning.1.0.0'}), 1784 ] 1785 if blob_namespace: 1786 elements.append(etree.Element('BLOB_NAMESPACE', attrib={'VALUE': blob_namespace})) 1787 if blob_name: 1788 elements.append(etree.Element('BLOB_NAME', attrib={'VALUE': blob_name})) 1789 else: 1790 elements.append(etree.Element('BLOB_NAME', attrib={'VALUE': desc_name})) 1791 if url: 1792 elements.append(etree.Element('PROFILE_URL', attrib={'VALUE': url})) 1793 return self._control_tag('RIB_INFO', 'PROFILE_DESC_DOWNLOAD', elements=elements) 1794 1795 def profile_list(self): 1796 """List all profile descriptors""" 1797 def process(data): 1798 if isinstance(data, dict): 1799 return data.values() 1800 return data 1801 return self._info_tag('RIB_INFO', 'PROFILE_LIST', 'PROFILE_DESC_LIST', process=process) 1802 1803 def hold_pwr_btn(self, toggle=None): 1804 """Press and hold the power button""" 1805 attrib = {} 1806 if toggle is not None: 1807 attrib['TOGGLE'] = ['No', 'Yes'][bool(toggle)] 1808 return self._control_tag('SERVER_INFO', 'HOLD_PWR_BTN', attrib=attrib) 1809 1810 def cold_boot_server(self): 1811 """Force a cold boot of the server""" 1812 return self._control_tag('SERVER_INFO', 'COLD_BOOT_SERVER') 1813 1814 def warm_boot_server(self): 1815 """Force a warm boot of the server""" 1816 return self._control_tag('SERVER_INFO', 'WARM_BOOT_SERVER') 1817 1818 def reset_rib(self): 1819 """Reset the iLO/RILOE board""" 1820 return self._control_tag('RIB_INFO', 'RESET_RIB') 1821 1822 def reset_server(self): 1823 """Power cycle the server""" 1824 return self._control_tag('SERVER_INFO', 'RESET_SERVER') 1825 1826 def send_snmp_test_trap(self): 1827 """Send an SNMP test trap to the configured alert destinations""" 1828 return self._control_tag('RIB_INFO', 'SEND_SNMP_TEST_TRAP') 1829 1830 def set_ahs_status(self, status): 1831 """Enable or disable AHS logging""" 1832 status = {True: 'enable', False: 'disable'}[status] 1833 return self._control_tag('RIB_INFO', 'SET_AHS_STATUS', attrib={'VALUE': status}) 1834 1835 def set_asset_tag(self, asset_tag): 1836 """Set the server asset tag""" 1837 return self._control_tag('SERVER_INFO', 'SET_ASSET_TAG', attrib={'VALUE': asset_tag}) 1838 1839 def set_critical_temp_remain_off(self, value): 1840 """Set whether the server will remain off after a critical temperature shutdown""" 1841 status = {True: 'Yes', False: 'No'}[value] 1842 return self._control_tag('SERVER_INFO', 'SET_CRITICAL_TEMP_REMAIN_OFF', attrib={'VALUE': value}) 1843 1844 def set_ers_direct_connect(self, user_id, password, proxy_url=None, 1845 proxy_port=None, proxy_username=None, proxy_password=None): 1846 """Register your iLO with HP Insigt Online using Direct Connect. Note 1847 that you must also call dc_registration_complete""" 1848 elements = [ 1849 etree.Element('ERS_HPP_USER_ID', attrib={'VALUE': str(user_id)}), 1850 etree.Element('ERS_HPP_PASSWORD', attrib={'VALUE': str(password)}), 1851 ] 1852 for key, value in locals().items(): 1853 if key.startswith('proxy_') and value is not None: 1854 elements.append(etree.Element('ERS_WEB_' + key, attrib={'VALUE': str(value)})) 1855 return self._control_tag('RIB_INFO', 'SET_ERS_DIRECT_CONNECT', elements=elements) 1856 1857 def set_ers_irs_connect(self, ers_destination_url, ers_destination_port): 1858 """Connect to an Insight Remote Support server""" 1859 elements = [ 1860 etree.Element('ERS_DESTINATION_URL', attrib={'VALUE': str(ers_destination_url)}), 1861 etree.Element('ERS_DESTINATION_PORT', attrib={'VALUE': str(ers_destination_port)}), 1862 ] 1863 return self._control_tag('RIB_INFO', 'SET_ERS_IRS_CONNECT', elements=elements) 1864 1865 def set_ers_web_proxy(self, proxy_url, proxy_port, proxy_username=None, 1866 proxy_password=None): 1867 """Register your iLO with HP Insigt Online using Direct Connect. Note 1868 that you must also call dc_registration_complete""" 1869 elements = [] 1870 for key, value in locals().items(): 1871 if key.startswith('proxy_') and value is not None: 1872 elements.append(etree.Element('ERS_WEB_' + key, attrib={'VALUE': str(value)})) 1873 return self._control_tag('RIB_INFO', 'SET_ERS_WEB_PROXY', elements=elements) 1874 1875 def set_federation_multicast(self, multicast_federation_enabled=True, multicast_discovery_enabled=True, 1876 multicast_announcement_interval=600, ipv6_multicast_scope="Site", multicast_ttl=5): 1877 """Set the Federation multicast configuration""" 1878 multicast_federation_enabled = {True: 'Yes', False: 'No'}[multicast_federation_enabled] 1879 multicast_discovery_enabled = {True: 'Yes', False: 'No'}[multicast_discovery_enabled] 1880 elements = [ 1881 etree.Element('MULTICAST_FEDERATION_ENABLED', attrib={'VALUE': multicast_federation_enabled}), 1882 etree.Element('MULTICAST_DISCOVERY_ENABLED', attrib={'VALUE': multicast_discovery_enabled}), 1883 etree.Element('MULTICAST_ANNOUNCEMENT_INTERVAL', attrib={'VALUE': str(multicast_announcement_interval)}), 1884 etree.Element('IPV6_MULTICAST_SCOPE', attrib={'VALUE': str(ipv6_multicast_scope)}), 1885 etree.Element('MULTICAST_TTL', attrib={'VALUE': str(multicast_ttl)}), 1886 ] 1887 return self._control_tag('RIB_INFO', 'SET_FEDERATION_MULTICAST', elements=elements) 1888 1889 1890 def set_language(self, lang_id): 1891 """Set the default language. Only EN, JA and ZH are supported""" 1892 return self._control_tag('RIB_INFO', 'SET_LANGUAGE', attrib={'LANG_ID': lang_id}) 1893 1894 def set_host_power(self, host_power=True): 1895 """Turn host power on or off""" 1896 power = ['No', 'Yes'][bool(host_power)] 1897 return self._control_tag('SERVER_INFO', 'SET_HOST_POWER', attrib={'HOST_POWER': power}) 1898 1899 def set_host_power_saver(self, host_power_saver): 1900 """Set the configuration of the ProLiant power regulator""" 1901 mapping = {'off': 1, 'min': 2, 'auto': 3, 'max': 4} 1902 host_power_saver = str(mapping.get(str(host_power_saver).lower(), host_power_saver)) 1903 return self._control_tag('SERVER_INFO', 'SET_HOST_POWER_SAVER', attrib={'HOST_POWER_SAVER': host_power_saver}) 1904 1905 def set_one_time_boot(self, device): 1906 """Set one time boot device, device should be one of normal, floppy, 1907 cdrom, hdd, usb, rbsu or network. Ilo 4 also supports EMB-MENU 1908 (Displays the default boot menu), EMB-ACU (Boots into ACU), 1909 EMB-HPSUM-AUTO (Boots HPSUM in automatic update mode), EMB-DIAGS 1910 (Launches Insight Diagnostics for Linux in interactive mode) and 1911 RBSU (Boots into the system RBSU)""" 1912 if not device.lower().startswith('boot'): 1913 device = device.upper() 1914 return self._control_tag('SERVER_INFO', 'SET_ONE_TIME_BOOT', attrib={'VALUE': device}) 1915 1916 def set_pending_boot_mode(self, boot_mode): 1917 """Set the boot mode for the next boot to UEFI or legacy""" 1918 return self._control_tag('SERVER_INFO', 'SET_PENDING_BOOT_MODE', attrib={'VALUE': boot_mode.upper()}) 1919 1920 def set_persistent_boot(self, devices): 1921 """Set persistent boot order, devices should be comma-separated""" 1922 elements = [] 1923 if isinstance(devices, basestring): 1924 devices = devices.split(',') 1925 for device in devices: 1926 if not device.lower().startswith('boot'): 1927 device = device.upper() 1928 elements.append(etree.Element('DEVICE', VALUE=device)) 1929 return self._control_tag('SERVER_INFO', 'SET_PERSISTENT_BOOT', elements=elements) 1930 1931 def set_pers_mouse_keyboard_enabled(self, enabled): 1932 """Enable/disable persistent mouse and keyboard""" 1933 enabled = {True: 'Yes', False: 'No'}.get(enabled,enabled) 1934 return self._control_tag('SERVER_INFO', 'SET_PERS_MOUSE_KEYBOARD_ENABLED', attrib={'VALUE': enabled}) 1935 1936 def set_pwreg(self, type, threshold=None, duration=None): 1937 """Set the power alert threshold""" 1938 elements = [etree.Element('PWRALERT', TYPE=type)] 1939 if type.lower() != "disabled": 1940 elements.append(etree.Element('PWRALERT_SETTINGS', THRESHOLD=str(threshold), DURATION=str(duration))) 1941 return self._control_tag('SERVER_INFO', 'SET_PWREG', elements=elements) 1942 1943 def set_power_cap(self, power_cap): 1944 """Set the power cap feature to a specific value""" 1945 return self._control_tag('SERVER_INFO', 'SET_POWER_CAP', attrib={'POWER_CAP': str(power_cap)}) 1946 1947 def set_security_msg(self, security_msg, security_msg_text=''): 1948 """Enables/disables the security message on the iLO login screen and sets its value""" 1949 enabled = str({True: 'Yes', False: 'No'}.get(security_msg, security_msg)) 1950 text = etree.Element('SECURITY_MSG_TEXT') 1951 text.append(CDATA(security_msg_text)) 1952 elements = (etree.Element('SECURITY_MSG', VALUE=enabled), text) 1953 return self._control_tag('RIB_INFO', 'SET_SECURITY_MSG', elements=elements) 1954 1955 def set_server_auto_pwr(self, setting): 1956 """Set the automatic power on delay setting. Valid settings are False, 1957 True (for minumum delay), 15, 30, 45 60 (for that amount of delay) 1958 or random (for a random delay of up to 60 seconds.)""" 1959 setting = str({True: 'Yes', False: 'No'}.get(setting, setting)) 1960 return self._control_tag('SERVER_INFO', 'SERVER_AUTO_PWR', attrib={'VALUE': setting}) 1961 1962 def set_server_fqdn(self, fqdn): 1963 """Set the fqdn of the server""" 1964 return self._control_tag('SERVER_INFO', 'SERVER_FQDN', attrib={"VALUE": fqdn}) 1965 1966 def set_server_name(self, name): 1967 """Set the name of the server""" 1968 try: 1969 return self._control_tag('SERVER_INFO', 'SERVER_NAME', attrib={"VALUE": name}) 1970 except IloError: 1971 # In their infinite wisdom, HP decided that only this tag should use value 1972 # instead of VALUE. And only for certain hardware/firmware combinations. 1973 # slowclap.mp3 1974 return self._control_tag('SERVER_INFO', 'SERVER_NAME', attrib={"value": name}) 1975 1976 def set_vf_status(self, boot_option="boot_once", write_protect=True): 1977 """Set the parameters of the RILOE virtual floppy specified virtual 1978 media. Valid boot options are boot_once, boot_always, no_boot, connect 1979 and disconnect.""" 1980 write_protect = ['NO', 'YES'][bool(write_protect)] 1981 elements = [ 1982 etree.Element('VF_BOOT_OPTION', value=boot_option.upper()), 1983 etree.Element('VF_WRITE_PROTECT', value=write_protect), 1984 ] 1985 return self._control_tag('RIB_INFO', 'SET_VF_STATUS', elements=elements) 1986 1987 def set_vm_status(self, device="cdrom", boot_option="boot_once", write_protect=True): 1988 """Set the parameters of the specified virtual media. Valid boot 1989 options are boot_once, boot_always, no_boot, connect and disconnect. 1990 Valid devices are floppy and cdrom""" 1991 1992 write_protect = ['NO', 'YES'][bool(write_protect)] 1993 elements = [ 1994 etree.Element('VM_BOOT_OPTION', value=boot_option.upper()), 1995 etree.Element('VM_WRITE_PROTECT', value=write_protect), 1996 ] 1997 return self._control_tag('RIB_INFO', 'SET_VM_STATUS', attrib={'DEVICE': device.upper()}, 1998 elements=elements) 1999 2000 def start_dir_test(self, dir_admin_distinguished_name, dir_admin_password, test_user_name, test_user_password): 2001 """Test directory authentication with the specified credentials""" 2002 vars = locals() 2003 del vars['self'] 2004 elements = [etree.Element(x, VALUE=vars[x]) for x in vars] 2005 return self._control_tag('DIR_INFO', 'START_DIR_TEST', elements=elements) 2006 2007 def trigger_bb_data(self, message_id, days): 2008 """Initiate AHS data submission to IRS. The submitted data will include 2009 the specified message ID and number of days of data""" 2010 return self._control_tag('RIB_INFO', 'TRIGGER_BB_DATA', attrib={'MESSAGE_ID': message_id, 'BB_DAYS': days}) 2011 2012 def trigger_l2_collection(self, message_id): 2013 """Initiate an L2 data collection submission to the Insight Remote Support server.""" 2014 element = etree.Element('MESSAGE_ID', attrib={'value': str(message_id)}) 2015 return self._control_tag('RIB_INFO', 'TRIGGER_L2_COLLECTION', elements=[element]) 2016 2017 def trigger_test_event(self, message_id): 2018 """Trigger a test service event submission to the Insight Remote Support server.""" 2019 element = etree.Element('MESSAGE_ID', attrib={'value': str(message_id)}) 2020 return self._control_tag('RIB_INFO', 'TRIGGER_TEST_EVENT', elements=[element]) 2021 2022 def uid_control(self, uid=False): 2023 """Turn the UID light on ("Yes") or off ("No")""" 2024 if isinstance(uid, basestring): 2025 uid = {'on': True, 'yes': True, 'off': False, 'no': False}.get(uid.lower(), uid) 2026 uid = ['No', 'Yes'][bool(uid)] 2027 return self._control_tag('SERVER_INFO', 'UID_CONTROL', attrib={"UID": uid.title()}) 2028 2029 def update_rib_firmware(self, filename=None, version=None, progress=None): 2030 """Upload new RIB firmware, either specified by filename (.bin or 2031 .scexe) or version number. Use "latest" as version number to 2032 download and use the latest available firmware. 2033 2034 API note: 2035 2036 As this function may take a while, you can choose to receive 2037 progress messages by passing a callable in the progress parameter. 2038 This callable will be called many times to inform you about upload 2039 and flash progress.""" 2040 2041 if self.delayed: 2042 raise IloError("Cannot run firmware update in delayed mode") 2043 2044 if self.read_response: 2045 raise IloError("Cannot run firmware update in read_response mode") 2046 2047 if not self.protocol: 2048 self._detect_protocol() 2049 2050 # Backwards compatibility 2051 if filename == 'latest': 2052 version = 'latest' 2053 filename = None 2054 2055 if filename and version: 2056 raise ValueError("Supply a filename or a version number, not both") 2057 2058 if not (filename or version): 2059 raise ValueError("Supply a filename or a version number") 2060 2061 current_version = self.get_fw_version() 2062 ilo = current_version['management_processor'].lower() 2063 2064 if not filename: 2065 config = hpilo_fw.config(self.firmware_mirror) 2066 if version == 'latest': 2067 if ilo not in config: 2068 raise IloError("Cannot update %s to the latest version automatically" % ilo) 2069 version = config[ilo]['version'] 2070 iversion = '%s %s' % (ilo, version) 2071 if iversion not in config: 2072 raise ValueError("Unknown firmware version: %s" % version) 2073 if current_version['firmware_version'] >= version: 2074 return "Already up-to-date" 2075 hpilo_fw.download(iversion, progress=progress) 2076 filename = config[iversion]['file'] 2077 else: 2078 filename = hpilo_fw.parse(filename, ilo) 2079 2080 fwlen = os.path.getsize(filename) 2081 root, inner = self._root_element('RIB_INFO', MODE='write') 2082 etree.SubElement(inner, 'TPM_ENABLED', VALUE='Yes') 2083 inner = etree.SubElement(inner, 'UPDATE_RIB_FIRMWARE', IMAGE_LOCATION=filename, IMAGE_LENGTH=str(fwlen)) 2084 if self.protocol == ILO_LOCAL: 2085 return self._request(root, progress)[1] 2086 elif self.protocol == ILO_RAW: 2087 inner.tail = '$EMBED:%s$' % filename 2088 return self._request(root, progress)[1] 2089 else: 2090 self._upload_file(filename, progress) 2091 return self._request(root, progress)[1] 2092 2093 def xmldata(self, item='all'): 2094 """Get basic discovery data which all iLO versions expose over 2095 unauthenticated https. The default item to query is 'all'. Despite 2096 its name, it does not return all information. To get license 2097 information, use 'cpqkey' as argument.""" 2098 if self.delayed: 2099 raise IloError("xmldata is not compatible with delayed mode") 2100 if item.lower() not in ('all', 'cpqkey'): 2101 raise IloError("unsupported xmldata argument '%s', must be 'all' or 'cpqkey'" % item) 2102 2103 if self.read_response: 2104 with open(self.read_response) as fd: 2105 data = fd.read() 2106 else: 2107 url = 'https://%s:%s/xmldata?item=%s' % (self.hostname, self.port, item) 2108 if hasattr(ssl, 'create_default_context'): 2109 ctx = ssl.create_default_context() 2110 ctx.check_hostname = False 2111 ctx.verify_mode = ssl.CERT_NONE 2112 opener = urllib2.build_opener(urllib2.ProxyHandler({}), urllib2.HTTPSHandler(context=ctx)) 2113 else: 2114 opener = urllib2.build_opener(urllib2.ProxyHandler({})) 2115 req = opener.open(url, None, self.timeout) 2116 data = req.read() 2117 self._debug(1, str(req.headers).rstrip() + "\n\n" + data.decode('ascii', 'iloxml_replace')) 2118 if self.save_response: 2119 fd = open(self.save_response, 'a') 2120 fd.write(data) 2121 fd.close() 2122 return self._element_children_to_dict(etree.fromstring(data)) 2123 2124 def _parse_infra2_XXXX(self, element, key, ctag): 2125 ret = {key: []} 2126 for elt in element: 2127 tag = elt.tag.lower() 2128 if tag == 'bays': 2129 ret['bays'] = self._element_to_list(elt) 2130 elif tag == ctag: 2131 ret[key].append(self._element_children_to_dict(elt)) 2132 else: 2133 ret[tag] = elt.text 2134 return {key: ret} 2135 2136 _parse_infra2_blades = lambda self, element: self._parse_infra2_XXXX(element, 'blades', 'blade') 2137 _parse_infra2_switches = lambda self, element: self._parse_infra2_XXXX(element, 'switches', 'switch') 2138 _parse_infra2_managers = lambda self, element: self._parse_infra2_XXXX(element, 'managers', 'manager') 2139 _parse_infra2_lcds = lambda self, element: self._parse_infra2_XXXX(element, 'lcds', 'lcd') 2140 _parse_infra2_fans = lambda self, element: self._parse_infra2_XXXX(element, 'fans', 'fan') 2141 2142 def _parse_infra2_power(self, element): 2143 ret = self._parse_infra2_XXXX(element, 'power', 'powersupply') 2144 ret['power']['powersupply'] = ret['power'].pop('power') 2145 return ret 2146 2147 def _parse_blade_portmap(self, element): 2148 ret = {'mezz': []} 2149 for elt in element: 2150 if elt.tag.lower() == 'mezz': 2151 ret['mezz'].append(self._element_children_to_dict(elt)) 2152 elif elt.tag.lower() == 'status': 2153 ret[elt.tag.lower()] = elt.text.strip() 2154 return {'portmap': ret} 2155 2156 def _parse_mezz_slot(self, element): 2157 ret = {'port': []} 2158 for elt in element: 2159 if elt.tag.lower() == 'port': 2160 ret['port'].append(self._element_children_to_dict(elt)) 2161 elif elt.tag.lower() == 'type': 2162 ret[elt.tag.lower()] = elt.text.strip() 2163 return {'slot': ret} 2164 2165 _parse_portmap_slot = _parse_mezz_slot 2166 2167 def _parse_mezz_device(self, element): 2168 ret = {'port': []} 2169 for elt in element: 2170 if elt.tag.lower() == 'port': 2171 ret['port'].append(self._element_children_to_dict(elt)) 2172 else: 2173 ret[elt.tag.lower()] = elt.text.strip() 2174 return {'device': ret} 2175 2176 def _parse_temps_temp(self, element): 2177 ret = {'thresholds': []} 2178 for elt in element: 2179 if elt.tag.lower() == 'threshold': 2180 ret['thresholds'].append(self._element_children_to_dict(elt)) 2181 else: 2182 ret[elt.tag.lower()] = elt.text 2183 return ret 2184 2185 xmldata_ectd = { 2186 'hsi': ('virtual',), 2187 'bladesystem': ('manager',), 2188 'infra2': ('diag', 'dim', 'vcm', 'vm'), 2189 'blade': ('bay', 'diag', 'portmap', 'power', 'vmstat'), 2190 'switch': ('bay', 'diag', 'portmap', 'power'), 2191 'manager': ('bay', 'diag', 'power'), 2192 'lcd': ('bay', 'diag'), 2193 'fan': ('bay',), 2194 'powersupply': ('bay', 'diag'), 2195 } 2196