Beispiel #1
0
    def _request_ilo(self, root):
        """Send RIBCL XML data to iLO.

        This function sends the XML request to the ILO and
        receives the output from ILO.

        :raises: IloConnectionError() if unable to send the request.
        """
        if self.port:
            urlstr = 'https://%s:%d/ribcl' % (self.host, self.port)
        else:
            urlstr = 'https://%s/ribcl' % (self.host)
        xml = self._serialize_xml(root)
        headers = {"Content-length": len(xml)}
        kwargs = {'headers': headers, 'data': xml}
        if self.cacert is not None:
            kwargs['verify'] = self.cacert
        else:
            kwargs['verify'] = False

        try:
            LOG.debug(self._("POST %(url)s with request data: "
                             "%(request_data)s"),
                      {'url': urlstr,
                       'request_data': MaskedRequestData(kwargs)})
            response = requests.post(urlstr, **kwargs)
            response.raise_for_status()
        except Exception as e:
            LOG.exception(self._("An error occurred while "
                                 "contacting iLO. Error: %s"), e)
            raise exception.IloConnectionError(e)
        return response.text
Beispiel #2
0
        def _fetch_response():

            url = retry_if_response_asks_for_redirection.url

            kwargs = {'headers': request_headers,
                      'data': json.dumps(request_body)}
            if self.cacert is not None:
                kwargs['verify'] = self.cacert
            else:
                kwargs['verify'] = False

            LOG.debug(self._('\n\tHTTP REQUEST: %(restreq_method)s'
                             '\n\tPATH: %(restreq_path)s'
                             '\n\tBODY: %(restreq_body)s'
                             '\n'),
                      {'restreq_method': operation,
                       'restreq_path': url.geturl(),
                       'restreq_body': request_body})

            request_method = getattr(requests, operation.lower())
            try:
                response = request_method(url.geturl(), **kwargs)
            except Exception as e:
                LOG.debug(self._("Unable to connect to iLO. %s"), e)
                raise exception.IloConnectionError(e)

            return response
Beispiel #3
0
def wait_for_ilo_after_reset(ilo_object):
    """Checks if iLO is up after reset."""

    retry_count = RETRY_COUNT
    # Delay for 10 sec, for the reset operation to take effect.
    time.sleep(10)

    while retry_count:
        try:
            ilo_object.get_product_name()
            break
        except exception.IloError:
            retry_count -= 1
            time.sleep(5)
    else:
        msg = ('iLO is not up after reset.')
        raise exception.IloConnectionError(msg)
Beispiel #4
0
    def _request_ilo(self, root):
        """Send RIBCL XML data to iLO.

        This function sends the XML request to the ILO and
        receives the output from ILO.

        :raises: IloConnectionError() if unable to send the request.
        """
        if self.port:
            urlstr = 'https://%s:%d/ribcl' % (self.host, self.port)
        else:
            urlstr = 'https://%s/ribcl' % (self.host)
        xml = self._serialize_xml(root)
        try:
            req = urllib2.Request(url=urlstr, data=xml)
            req.add_header("Content-length", len(xml))
            data = urllib2.urlopen(req).read()
        except (ValueError, urllib2.URLError, urllib2.HTTPError) as e:
            raise exception.IloConnectionError(e)
        return data
Beispiel #5
0
    def _rest_op(self, operation, suburi, request_headers, request_body):
        """Generic REST Operation handler."""

        url = urlparse.urlparse('https://' + self.host + suburi)

        if request_headers is None:
            request_headers = dict()

        # Use self.login/self.password and Basic Auth
        if self.login is not None and self.password is not None:
            hr = "BASIC " + base64.b64encode(self.login + ":" + self.password)
            request_headers['Authorization'] = hr

        redir_count = 4
        while redir_count:
            conn = None
            if url.scheme == 'https':
                conn = httplib.HTTPSConnection(host=url.netloc, strict=True)
            elif url.scheme == 'http':
                conn = httplib.HTTPConnection(host=url.netloc, strict=True)

            try:
                conn.request(operation,
                             url.path,
                             headers=request_headers,
                             body=json.dumps(request_body))
                resp = conn.getresponse()
                body = resp.read()
            except Exception as e:
                raise exception.IloConnectionError(e)

            # NOTE:Do not assume every HTTP operation will return a JSON body.
            # For example, ExtendedError structures are only required for
            # HTTP 400 errors and are optional elsewhere as they are mostly
            # redundant for many of the other HTTP status code. In particular,
            # 200 OK responses should not have to return any body.

            # NOTE:  this makes sure the headers names are all lower cases
            # because HTTP says they are case insensitive
            headers = dict((x.lower(), y) for x, y in resp.getheaders())

            # Follow HTTP redirect
            if resp.status == 301 and 'location' in headers:
                url = urlparse.urlparse(headers['location'])
                redir_count -= 1
            else:
                break

        response = dict()
        try:
            if body:
                response = json.loads(body.decode('utf-8'))
        except ValueError:
            # if it doesn't decode as json
            # NOTE:  resources may return gzipped content
            # try to decode as gzip (we should check the headers for
            # Content-Encoding=gzip)
            try:
                gzipper = gzip.GzipFile(fileobj=StringIO.StringIO(body))
                uncompressed_string = gzipper.read().decode('UTF-8')
                response = json.loads(uncompressed_string)
            except Exception as e:
                raise exception.IloError(e)

        return resp.status, headers, response
Beispiel #6
0
    def _rest_op(self, operation, suburi, request_headers, request_body):
        """Generic REST Operation handler."""

        url = urlparse.urlparse('https://' + self.host + suburi)
        # Used for logging on redirection error.
        start_url = url.geturl()

        LOG.debug(self._("%(operation)s %(url)s"),
                  {'operation': operation, 'url': start_url})

        if request_headers is None or not isinstance(request_headers, dict):
            request_headers = {}

        # Use self.login/self.password and Basic Auth
        if self.login is not None and self.password is not None:
            auth_data = self.login + ":" + self.password
            hr = "BASIC " + base64.b64encode(
                auth_data.encode('ascii')).decode("utf-8")
            request_headers['Authorization'] = hr

        if request_body is not None:
            if (isinstance(request_body, dict)
                    or isinstance(request_body, list)):
                request_headers['Content-Type'] = 'application/json'
            else:
                request_headers['Content-Type'] = ('application/'
                                                   'x-www-form-urlencoded')

        """Helper methods to retry and keep retrying on redirection - START"""

        def retry_if_response_asks_for_redirection(response):
            # NOTE:Do not assume every HTTP operation will return a JSON
            # request_body. For example, ExtendedError structures are only
            # required for HTTP 400 errors and are optional elsewhere as they
            # are mostly redundant for many of the other HTTP status code.
            # In particular, 200 OK responses should not have to return any
            # request_body.

            # NOTE:  this makes sure the headers names are all lower cases
            # because HTTP says they are case insensitive
            # Follow HTTP redirect
            if response.status_code == 301 and 'location' in response.headers:
                retry_if_response_asks_for_redirection.url = (
                    urlparse.urlparse(response.headers['location']))
                LOG.debug(self._("Request redirected to %s."),
                          retry_if_response_asks_for_redirection.url.geturl())
                return True
            return False

        @retrying.retry(
            # Note(deray): Return True if we should retry, False otherwise.
            # In our case, when the url response we receive asks for
            # redirection then we retry.
            retry_on_result=retry_if_response_asks_for_redirection,
            # Note(deray): Return True if we should retry, False otherwise.
            # In our case, when it's an IloConnectionError we don't retry.
            # ``requests`` already takes care of issuing max number of
            # retries if the URL service is unavailable.
            retry_on_exception=(
                lambda e: not isinstance(e, exception.IloConnectionError)),
            stop_max_attempt_number=REDIRECTION_ATTEMPTS)
        def _fetch_response():

            url = retry_if_response_asks_for_redirection.url

            kwargs = {'headers': request_headers,
                      'data': json.dumps(request_body)}
            if self.cacert is not None:
                kwargs['verify'] = self.cacert
            else:
                kwargs['verify'] = False

            LOG.debug(self._('\n\tHTTP REQUEST: %(restreq_method)s'
                             '\n\tPATH: %(restreq_path)s'
                             '\n\tBODY: %(restreq_body)s'
                             '\n'),
                      {'restreq_method': operation,
                       'restreq_path': url.geturl(),
                       'restreq_body': request_body})

            request_method = getattr(requests, operation.lower())
            try:
                response = request_method(url.geturl(), **kwargs)
            except Exception as e:
                LOG.debug(self._("Unable to connect to iLO. %s"), e)
                raise exception.IloConnectionError(e)

            return response

        """Helper methods to retry and keep retrying on redirection - END"""

        try:
            # Note(deray): This is a trick to use the function attributes
            # to overwrite variable/s (in our case ``url``) and use the
            # modified one in nested functions, i.e. :func:`_fetch_response`
            # and :func:`retry_if_response_asks_for_redirection`
            retry_if_response_asks_for_redirection.url = url

            response = _fetch_response()
        except retrying.RetryError as e:
            # Redirected for REDIRECTION_ATTEMPTS - th time. Throw error
            msg = (self._("URL Redirected %(times)s times continuously. "
                          "URL used: %(start_url)s More info: %(error)s") %
                   {'start_url': start_url, 'times': REDIRECTION_ATTEMPTS,
                    'error': str(e)})
            LOG.debug(msg)
            raise exception.IloConnectionError(msg)

        response_body = {}
        if response.text:
            try:
                response_body = json.loads(response.text)
            except (TypeError, ValueError):
                # Note(deray): If it doesn't decode as json, then
                # resources may return gzipped content.
                # ``json.loads`` on python3 raises TypeError when
                # ``response.text`` is gzipped one.
                response_body = (
                    self._get_response_body_from_gzipped_content(url,
                                                                 response))

        LOG.debug(self._('\n\tHTTP RESPONSE for %(restreq_path)s:'
                         '\n\tCode: %(status_code)s'
                         '\n\tResponse Body: %(response_body)s'
                         '\n'),
                  {'restreq_path': url.geturl(),
                   'status_code': response.status_code,
                   'response_body': response_body})
        return response.status_code, response.headers, response_body
    def _get_socket(self, sslversion=ssl.PROTOCOL_TLSv1):
        """Sets up an https connection and do an HTTP/raw socket request

        :param sslversion: version of ssl session
        :raises: IloConnectionError, for connection failures
        :returns: ssl wrapped socket object
        """
        err = None
        sock = None
        try:
            for res in socket.getaddrinfo(self.hostname, self.port, 0,
                                          socket.SOCK_STREAM):
                af, socktype, proto, canonname, sa = res
                try:
                    sock = socket.socket(af, socktype, proto)
                    sock.settimeout(self.timeout)
                    # Connecting to {self.hostname} at port {self.port}
                    sock.connect(sa)
                except socket.timeout:
                    if sock is not None:
                        sock.close()
                    err = exception.IloConnectionError(
                        "Timeout connecting to %(hostname)s:%(port)d" % {
                            'hostname': self.hostname,
                            'port': self.port
                        })
                except socket.error:
                    if sock is not None:
                        sock.close()
                    e = sys.exc_info()[1]
                    err = exception.IloConnectionError(
                        "Error connecting to %(hostname)s:%(port)d : %(error)s"
                        % {
                            'hostname': self.hostname,
                            'port': self.port,
                            'error': str(e)
                        })
        except Exception:
            raise exception.IloConnectionError("Unable to resolve %s" %
                                               self.hostname)

        if err is not None:
            raise err

        # wrapping the socket over ssl session
        try:
            return ssl.wrap_socket(sock, ssl_version=sslversion)
        except socket.sslerror:
            e = sys.exc_info()[1]
            msg = (getattr(e, 'reason', None) or getattr(e, 'message', None))
            # Some older iLO s don't support TLSv1, retry with SSLv3
            if ('wrong version number' in msg) and (sslversion
                                                    == ssl.PROTOCOL_TLSv1):

                return self._get_socket(ssl.PROTOCOL_SSLv3)

            raise exception.IloConnectionError(
                "Cannot establish ssl session with %(hostname)s:%(port)d : "
                "%(error)s" % {
                    'hostname': self.hostname,
                    'port': self.port,
                    'error': str(e)
                })
    def upload_file_to(self, addressinfo, timeout):
        """Uploads the raw firmware file to iLO

        Uploads the raw firmware file (already set as attribute in
        FirmwareImageControllerBase constructor) to iLO, whose address
        information is passed to this method.
        :param addressinfo: tuple of hostname and port of the iLO
        :param timeout: timeout in secs, used for connecting to iLO
        :raises: IloInvalidInputError, if raw firmware file not found
        :raises: IloError, for other internal problems
        :returns: the cookie so sent back from iLO on successful upload
        """
        self.hostname, self.port = addressinfo
        self.timeout = timeout
        filename = self.fw_file

        firmware = open(filename, 'rb').read()
        # generate boundary
        boundary = b('------hpiLO3t' + str(random.randint(100000, 1000000)) +
                     'z')

        while boundary in firmware:
            boundary = b('------hpiLO3t' +
                         str(random.randint(100000, 1000000)) + 'z')
        # generate body parts
        parts = [
            # body1
            b("--") + boundary + b("""\r\nContent-Disposition: form-data; """
                                   """name="fileType"\r\n\r\n"""),
            # body2
            b("\r\n--") + boundary +
            b('''\r\nContent-Disposition: form-data; name="fwimgfile"; '''
              '''filename="''') + b(filename) +
            b('''"\r\nContent-Type: application/octet-stream\r\n\r\n'''),
            # firmware image
            firmware,
            # body3
            b("\r\n--") + boundary + b("--\r\n"),
        ]
        total_bytes = sum([len(x) for x in parts])
        sock = self._get_socket()

        # send the firmware image
        sock.write(
            b(self.HTTP_UPLOAD_HEADER %
              (total_bytes, boundary.decode('ascii'))))
        for part in parts:
            sock.write(part)

        data = ''
        try:
            while True:
                d = sock.read()
                data += d.decode('latin-1')
                if not d:
                    break
        except socket.sslerror:  # Connection closed
            e = sys.exc_info()[1]
            if not data:
                raise exception.IloConnectionError(
                    "Communication with %(hostname)s:%(port)d failed: "
                    "%(error)s" % {
                        'hostname': self.hostname,
                        'port': self.port,
                        'error': str(e)
                    })

        # Received len(data) bytes
        cookie_match = re.search('Set-Cookie: *(.*)', data)
        if not cookie_match:
            raise exception.IloError("Uploading of file: %s failed due "
                                     "to unknown reason." % filename)
        # return the cookie
        return cookie_match.group(1)