コード例 #1
0
def call_inspector(data, failures):
    """Post data to inspector."""
    data['error'] = failures.get_error()

    LOG.info('posting collected data to %s', CONF.inspection_callback_url)
    LOG.debug('collected data: %s',
              {k: v
               for k, v in data.items() if k not in _NO_LOGGING_FIELDS})

    encoder = encoding.RESTJSONEncoder()
    data = encoder.encode(data)
    verify, cert = utils.get_ssl_client_options(CONF)

    @tenacity.retry(retry=tenacity.retry_if_exception_type(
        requests.exceptions.ConnectionError),
                    stop=tenacity.stop_after_attempt(_RETRY_ATTEMPTS),
                    wait=tenacity.wait_fixed(_RETRY_WAIT),
                    reraise=True)
    def _post_to_inspector():
        return requests.post(CONF.inspection_callback_url,
                             data=data,
                             verify=verify,
                             cert=cert)

    resp = _post_to_inspector()
    if resp.status_code >= 400:
        LOG.error('inspector %s error %d: %s, proceeding with lookup',
                  CONF.inspection_callback_url, resp.status_code,
                  resp.content.decode('utf-8'))
        return

    return resp.json()
コード例 #2
0
def _download_with_proxy(image_info, url, image_id):
    """Opens a download stream for the given URL.

    :param image_info: Image information dictionary.
    :param url: The URL string to request the image from.
    :param image_id: Image ID or URL for logging.

    :raises: ImageDownloadError if the download stream was not started
             properly.
    """
    no_proxy = image_info.get('no_proxy')
    if no_proxy:
        os.environ['no_proxy'] = no_proxy
    proxies = image_info.get('proxies', {})
    verify, cert = utils.get_ssl_client_options(CONF)
    resp = requests.get(url,
                        stream=True,
                        proxies=proxies,
                        verify=verify,
                        cert=cert)
    if resp.status_code != 200:
        msg = ('Received status code {} from {}, expected 200. Response '
               'body: {}').format(resp.status_code, url, resp.text)
        raise errors.ImageDownloadError(image_id, msg)
    return resp
コード例 #3
0
ファイル: inspector.py プロジェクト: tyws/ipa-custom
def config_raid(data):
    # server_type = raid_utils.get_type_by_properties(data)
    # if server_type not in raid_utils.VALID_TYPE:
    #     LOG.error('Unknown server type, and can not configure RAID default.')
    #     return

    sn = data.get('inventory').get('system_vendor').serial_number
    verify, cert = utils.get_ssl_client_options(CONF)

    # retrieve raid configuration info from arobot server
    raid_get_url = CONF.arobot_callback_url + ('/raid_conf/%s' % sn)
    resp = requests.get(raid_get_url, cert=cert, verify=verify)
    config = resp.json()

    if resp.status_code >= 400:
        LOG.error('error fetching raid configuration')
        return

    if config is None:
        LOG.error('configuration should never be None')
        return

    if config['is_ok']:
        LOG.info('sn: %s has been already been configured. Pass' % config.get('sn', ''))
        return

    # start configuring if raid has not been configured properly
    LOG.info("Start configuring RAID")

    raid_config = raid_utils.config_raid()
    # call back and save raid configurations
    raid_post_url = CONF.arobot_callback_url + '/raid_conf'
    json = {
        'sn': sn,
        'config': raid_config
    }

    # update contents of inventory.disks
    # because hardware.GenericHardwareManager will always
    # be loaded before raid properly configured
    data['inventory']['disks'] = hardware.GenericHardwareManager().list_block_devices()

    # call back to ironic-inspector
    LOG.info("Posting RAID configuration back to %s", raid_post_url)
    while True:
        try:
            resp = requests.post(raid_post_url, json=json, cert=cert, verify=verify)
            if resp.status_code >= 400:
                LOG.error("arobot raid error %d: %s", resp.status_code, resp.content.decode('utf-8'))
        except Exception as e:
            LOG.error(e)
            time.sleep(5)
            continue
        LOG.info('request ok')
        break
コード例 #4
0
def _download_with_proxy(image_info, url, image_id):
    """Opens a download stream for the given URL.

    :param image_info: Image information dictionary.
    :param url: The URL string to request the image from.
    :param image_id: Image ID or URL for logging.

    :raises: ImageDownloadError if the download stream was not started
             properly.
    """
    no_proxy = image_info.get('no_proxy')
    if no_proxy:
        os.environ['no_proxy'] = no_proxy
    proxies = image_info.get('proxies', {})
    verify, cert = utils.get_ssl_client_options(CONF)
    resp = None
    for attempt in range(CONF.image_download_connection_retries + 1):
        try:
            # NOTE(TheJulia) The get request below does the following:
            # * Performs dns lookups, if necessary
            # * Opens the TCP socket to the remote host
            # * Negotiates TLS, if applicable
            # * Checks cert validity, if necessary, which may be
            #   more tcp socket connections.
            # * Issues the get request and then returns back to the caller the
            #   handler which is used to stream the data into the agent.
            # While this all may be at risk of transitory interrupts, most of
            # these socket will have timeouts applied to them, although not
            # exactly just as the timeout value exists. The risk in transitory
            # failure is more so once we've started the download and we are
            # processing the incoming data.
            resp = requests.get(url,
                                stream=True,
                                proxies=proxies,
                                verify=verify,
                                cert=cert,
                                timeout=CONF.image_download_connection_timeout)
            if resp.status_code != 200:
                msg = ('Received status code {} from {}, expected 200. '
                       'Response body: {}').format(resp.status_code, url,
                                                   resp.text)
                raise errors.ImageDownloadError(image_id, msg)
        except (errors.ImageDownloadError, requests.RequestException) as e:
            if (attempt == CONF.image_download_connection_retries
                    # NOTE(dtantsur): do not retry 4xx status codes
                    or (resp and resp.status_code < 500)):
                raise
            else:
                LOG.warning('Unable to connect to %s, retrying. Error: %s',
                            url, e)
                time.sleep(CONF.image_download_connection_retry_interval)
        else:
            break
    return resp
コード例 #5
0
    def test_get_ssl_client_options(self):
        # defaults
        conf = mock.Mock(insecure=False, cafile=None,
                         keyfile=None, certfile=None)
        self.assertEqual((True, None), utils.get_ssl_client_options(conf))

        # insecure=True overrides cafile
        conf = mock.Mock(insecure=True, cafile='spam',
                         keyfile=None, certfile=None)
        self.assertEqual((False, None), utils.get_ssl_client_options(conf))

        # cafile returned as verify when not insecure
        conf = mock.Mock(insecure=False, cafile='spam',
                         keyfile=None, certfile=None)
        self.assertEqual(('spam', None), utils.get_ssl_client_options(conf))

        # only both certfile and keyfile produce non-None result
        conf = mock.Mock(insecure=False, cafile=None,
                         keyfile=None, certfile='ham')
        self.assertEqual((True, None), utils.get_ssl_client_options(conf))

        conf = mock.Mock(insecure=False, cafile=None,
                         keyfile='ham', certfile=None)
        self.assertEqual((True, None), utils.get_ssl_client_options(conf))

        conf = mock.Mock(insecure=False, cafile=None,
                         keyfile='spam', certfile='ham')
        self.assertEqual((True, ('ham', 'spam')),
                         utils.get_ssl_client_options(conf))
コード例 #6
0
    def test_get_ssl_client_options(self):
        # defaults
        conf = mock.Mock(insecure=False, cafile=None,
                         keyfile=None, certfile=None)
        self.assertEqual((True, None), utils.get_ssl_client_options(conf))

        # insecure=True overrides cafile
        conf = mock.Mock(insecure=True, cafile='spam',
                         keyfile=None, certfile=None)
        self.assertEqual((False, None), utils.get_ssl_client_options(conf))

        # cafile returned as verify when not insecure
        conf = mock.Mock(insecure=False, cafile='spam',
                         keyfile=None, certfile=None)
        self.assertEqual(('spam', None), utils.get_ssl_client_options(conf))

        # only both certfile and keyfile produce non-None result
        conf = mock.Mock(insecure=False, cafile=None,
                         keyfile=None, certfile='ham')
        self.assertEqual((True, None), utils.get_ssl_client_options(conf))

        conf = mock.Mock(insecure=False, cafile=None,
                         keyfile='ham', certfile=None)
        self.assertEqual((True, None), utils.get_ssl_client_options(conf))

        conf = mock.Mock(insecure=False, cafile=None,
                         keyfile='spam', certfile='ham')
        self.assertEqual((True, ('ham', 'spam')),
                         utils.get_ssl_client_options(conf))
コード例 #7
0
ファイル: inspector.py プロジェクト: tyws/ipa-custom
def tell_arobot_ipmi(sn):
    LOG.info('Tell arobot ipmi config successfully %s', CONF.arobot_callback_url)

    verify, cert = utils.get_ssl_client_options(CONF)
    # arobot_callback_url like http://172.23.4.111:9876/v1
    ipmi_get_url = CONF.arobot_callback_url + '/ipmi_conf/' + sn
    while True:
        try:
            resp = requests.put(ipmi_get_url, verify=verify, cert=cert)
        except Exception as e:
            LOG.info('Got exception %s', e)
            continue
        else:
            LOG.info('Put ok')
            break
コード例 #8
0
    def _request(self, method, path, data=None, headers=None, **kwargs):
        request_url = '{api_url}{path}'.format(api_url=self.api_url, path=path)

        if data is not None:
            data = self.encoder.encode(data)

        headers = headers or {}
        headers.update({
            'Content-Type': 'application/json',
            'Accept': 'application/json',
        })

        verify, cert = utils.get_ssl_client_options(CONF)
        return self.session.request(method,
                                    request_url,
                                    headers=headers,
                                    data=data,
                                    verify=verify,
                                    cert=cert,
                                    **kwargs)
コード例 #9
0
    def _request(self, method, path, data=None, headers=None, **kwargs):
        request_url = '{api_url}{path}'.format(api_url=self.api_url, path=path)

        if data is not None:
            data = self.encoder.encode(data)

        headers = headers or {}
        headers.update({
            'Content-Type': 'application/json',
            'Accept': 'application/json',
        })

        verify, cert = utils.get_ssl_client_options(CONF)
        return self.session.request(method,
                                    request_url,
                                    headers=headers,
                                    data=data,
                                    verify=verify,
                                    cert=cert,
                                    **kwargs)
コード例 #10
0
ファイル: inspector.py プロジェクト: tyws/ipa-custom
def call_inspector(data, failures):
    """Post data to inspector."""
    data['error'] = failures.get_error()

    LOG.info('posting collected data to %s', CONF.inspection_callback_url)
    LOG.debug('collected data: %s',
              {k: v for k, v in data.items() if k not in _NO_LOGGING_FIELDS})

    encoder = encoding.RESTJSONEncoder()
    data = encoder.encode(data)

    verify, cert = utils.get_ssl_client_options(CONF)
    resp = requests.post(CONF.inspection_callback_url, data=data,
                         verify=verify, cert=cert)
    if resp.status_code >= 400:
        LOG.error('inspector error %d: %s, proceeding with lookup',
                  resp.status_code, resp.content.decode('utf-8'))
        return

    return resp.json()
コード例 #11
0
def call_inspector(data, failures):
    """Post data to inspector."""
    data['error'] = failures.get_error()

    LOG.info('posting collected data to %s', CONF.inspection_callback_url)
    LOG.debug('collected data: %s',
              {k: v for k, v in data.items() if k not in _NO_LOGGING_FIELDS})

    encoder = encoding.RESTJSONEncoder()
    data = encoder.encode(data)

    verify, cert = utils.get_ssl_client_options(CONF)
    resp = requests.post(CONF.inspection_callback_url, data=data,
                         verify=verify, cert=cert)
    if resp.status_code >= 400:
        LOG.error('inspector error %d: %s, proceeding with lookup',
                  resp.status_code, resp.content.decode('utf-8'))
        return

    return resp.json()
コード例 #12
0
ファイル: inspector.py プロジェクト: tyws/ipa-custom
def call_arobot(sn):
    LOG.info('Getting ipmi conf from %s', CONF.arobot_callback_url)

    verify, cert = utils.get_ssl_client_options(CONF)
    # arobot_callback_url like http://172.23.4.111:9876/v1
    ipmi_get_url = CONF.arobot_callback_url + '/ipmi_conf/' + sn
    resp = requests.get(ipmi_get_url, verify=verify, cert=cert)
    if resp.status_code >= 400:
        LOG.error('arobot ipmi error %d: %s',
                  resp.status_code  , resp.content.decode('utf-8'))
        return

    ret = resp.json()
    if ret.get('return_value') == 'NotFound' or \
        ret.get('return_value') == 'UnknownERR':
        LOG.warning('arobot api call ok, but no valid ipmi conf. Loop go on!')
        return
    elif ret.get('return_value') == 'Success':
        LOG.warning('arobot api call ok, but already conf OK. Break loop!')

    return ret
コード例 #13
0
def _download_with_proxy(image_info, url, image_id):
    """Opens a download stream for the given URL.

    :param image_info: Image information dictionary.
    :param url: The URL string to request the image from.
    :param image_id: Image ID or URL for logging.

    :raises: ImageDownloadError if the download stream was not started
             properly.
    """
    no_proxy = image_info.get('no_proxy')
    if no_proxy:
        os.environ['no_proxy'] = no_proxy
    proxies = image_info.get('proxies', {})
    verify, cert = utils.get_ssl_client_options(CONF)
    resp = requests.get(url, stream=True, proxies=proxies,
                        verify=verify, cert=cert)
    if resp.status_code != 200:
        msg = ('Received status code {} from {}, expected 200. Response '
               'body: {}').format(resp.status_code, url, resp.text)
        raise errors.ImageDownloadError(image_id, msg)
    return resp
コード例 #14
0
def _download_with_proxy(image_info, url, image_id):
    """Opens a download stream for the given URL.

    :param image_info: Image information dictionary.
    :param url: The URL string to request the image from.
    :param image_id: Image ID or URL for logging.

    :raises: ImageDownloadError if the download stream was not started
             properly.
    """
    no_proxy = image_info.get('no_proxy')
    if no_proxy:
        os.environ['no_proxy'] = no_proxy
    proxies = image_info.get('proxies', {})
    verify, cert = utils.get_ssl_client_options(CONF)
    resp = None
    for attempt in range(CONF.image_download_connection_retries + 1):
        try:
            resp = requests.get(url, stream=True, proxies=proxies,
                                verify=verify, cert=cert,
                                timeout=CONF.image_download_connection_timeout)
            if resp.status_code != 200:
                msg = ('Received status code {} from {}, expected 200. '
                       'Response body: {}').format(resp.status_code, url,
                                                   resp.text)
                raise errors.ImageDownloadError(image_id, msg)
        except (errors.ImageDownloadError, requests.RequestException) as e:
            if (attempt == CONF.image_download_connection_retries
                    # NOTE(dtantsur): do not retry 4xx status codes
                    or (resp and resp.status_code < 500)):
                raise
            else:
                LOG.warning('Unable to connect to %s, retrying. Error: %s',
                            url, e)
                time.sleep(CONF.image_download_connection_retry_interval)
        else:
            break
    return resp
コード例 #15
0
def get_configdrive(configdrive, node_uuid, tempdir=None):
    """Get the information about size and location of the configdrive.

    :param configdrive: Base64 encoded Gzipped configdrive content or
        configdrive HTTP URL.
    :param node_uuid: Node's uuid. Used for logging.
    :param tempdir: temporary directory for the temporary configdrive file
    :raises: InstanceDeployFailure if it can't download or decode the
       config drive.
    :returns: A tuple with the size in MiB and path to the uncompressed
        configdrive file.

    """
    # Check if the configdrive option is a HTTP URL or the content directly
    is_url = utils.is_http_url(configdrive)
    if is_url:
        verify, cert = ipa_utils.get_ssl_client_options(CONF)
        timeout = CONF.image_download_connection_timeout
        # TODO(dtantsur): support proxy parameters from instance_info
        try:
            resp = requests.get(configdrive, verify=verify, cert=cert,
                                timeout=timeout)
        except requests.exceptions.RequestException as e:
            raise exception.InstanceDeployFailure(
                "Can't download the configdrive content for node %(node)s "
                "from '%(url)s'. Reason: %(reason)s" %
                {'node': node_uuid, 'url': configdrive, 'reason': e})

        if resp.status_code >= 400:
            raise exception.InstanceDeployFailure(
                "Can't download the configdrive content for node %(node)s "
                "from '%(url)s'. Got status code %(code)s, response "
                "body %(body)s" %
                {'node': node_uuid, 'url': configdrive,
                 'code': resp.status_code, 'body': resp.text})

        data = resp.content
    else:
        data = configdrive

    configdrive_file = tempfile.NamedTemporaryFile(delete=False,
                                                   prefix='configdrive',
                                                   dir=tempdir)

    try:
        data = io.BytesIO(base64.b64decode(data))
    except Exception as exc:
        if isinstance(data, bytes):
            LOG.debug('Config drive for node %(node)s is not base64 encoded '
                      '(%(error)s), assuming binary',
                      {'node': node_uuid, 'error': exc})
            configdrive_mb = int(math.ceil(len(data) / units.Mi))
            configdrive_file.write(data)
            configdrive_file.close()
            return (configdrive_mb, configdrive_file.name)
        else:
            configdrive_file.close()
            utils.unlink_without_raise(configdrive_file.name)

            error_msg = ('Config drive for node %(node)s is not base64 '
                         'encoded or the content is malformed. '
                         '%(cls)s: %(err)s.'
                         % {'node': node_uuid, 'err': exc,
                            'cls': type(exc).__name__})
            if is_url:
                error_msg += ' Downloaded from "%s".' % configdrive
            raise exception.InstanceDeployFailure(error_msg)

    configdrive_mb = 0
    with gzip.GzipFile('configdrive', 'rb', fileobj=data) as gunzipped:
        try:
            shutil.copyfileobj(gunzipped, configdrive_file)
        except EnvironmentError as e:
            # Delete the created file
            utils.unlink_without_raise(configdrive_file.name)
            raise exception.InstanceDeployFailure(
                'Encountered error while decompressing and writing '
                'config drive for node %(node)s. Error: %(exc)s' %
                {'node': node_uuid, 'exc': e})
        else:
            # Get the file size and convert to MiB
            configdrive_file.seek(0, os.SEEK_END)
            bytes_ = configdrive_file.tell()
            configdrive_mb = int(math.ceil(float(bytes_) / units.Mi))
        finally:
            configdrive_file.close()

        return (configdrive_mb, configdrive_file.name)