Ejemplo n.º 1
0
def inspect():
    """Optionally run inspection on the current node.

    If ``inspection_callback_url`` is set in the configuration, get
    the hardware inventory from the node and post it back to the inspector.

    :return: node UUID if inspection was successful, None if associated node
             was not found in inspector cache. None is also returned if
             inspector support is not enabled.
    """
    if not CONF.inspection_callback_url:
        LOG.info('Inspection is disabled, skipping')
        return

    if CONF.inspection_callback_url == 'mdns':
        LOG.debug('Fetching the inspection URL from mDNS')
        url, params = mdns.get_endpoint('baremetal-introspection')
        # We expect a proper catalog URL, which doesn't include any path.
        CONF.set_override('inspection_callback_url',
                          url.rstrip('/') + '/v1/continue')
        config.override(params)

    collector_names = [
        x.strip() for x in CONF.inspection_collectors.split(',') if x.strip()
    ]
    LOG.info('inspection is enabled with collectors %s', collector_names)

    # NOTE(dtantsur): inspection process tries to delay raising any exceptions
    # until after we posted some data back to inspector. This is because
    # inspection is run automatically on (mostly) unknown nodes, so if it
    # fails, we don't have much information for debugging.
    failures = utils.AccumulatedFailures(exc_class=errors.InspectionError)
    data = {}

    try:
        ext_mgr = extension_manager(collector_names)
        collectors = [(ext.name, ext.plugin) for ext in ext_mgr]
    except Exception as exc:
        with excutils.save_and_reraise_exception():
            failures.add(exc)
            call_inspector(data, failures)

    for name, collector in collectors:
        try:
            collector(data, failures)
        except Exception as exc:
            # No reraise here, try to keep going
            failures.add('collector %s failed: %s', name, exc)

    resp = call_inspector(data, failures)

    # Now raise everything we were delaying
    failures.raise_if_needed()

    if resp is None:
        raise errors.InspectionError('stopping inspection, as inspector '
                                     'returned an error')

    LOG.info('inspection finished successfully')
    return resp.get('uuid')
Ejemplo n.º 2
0
def inspect():
    """Optionally run inspection on the current node.

    If ``inspection_callback_url`` is set in the configuration, get
    the hardware inventory from the node and post it back to the inspector.

    :return: node UUID if inspection was successful, None if associated node
             was not found in inspector cache. None is also returned if
             inspector support is not enabled.
    """
    if not CONF.inspection_callback_url:
        LOG.info('Inspection is disabled, skipping')
        return

    if CONF.inspection_callback_url == 'mdns':
        LOG.debug('Fetching the inspection URL from mDNS')
        url, params = mdns.get_endpoint('baremetal-introspection')
        # We expect a proper catalog URL, which doesn't include any path.
        CONF.set_override('inspection_callback_url',
                          url.rstrip('/') + '/v1/continue')
        config.override(params)

    collector_names = [x.strip() for x in CONF.inspection_collectors.split(',')
                       if x.strip()]
    LOG.info('inspection is enabled with collectors %s', collector_names)

    # NOTE(dtantsur): inspection process tries to delay raising any exceptions
    # until after we posted some data back to inspector. This is because
    # inspection is run automatically on (mostly) unknown nodes, so if it
    # fails, we don't have much information for debugging.
    failures = utils.AccumulatedFailures(exc_class=errors.InspectionError)
    data = {}

    try:
        ext_mgr = extension_manager(collector_names)
        collectors = [(ext.name, ext.plugin) for ext in ext_mgr]
    except Exception as exc:
        with excutils.save_and_reraise_exception():
            failures.add(exc)
            call_inspector(data, failures)

    for name, collector in collectors:
        try:
            collector(data, failures)
        except Exception as exc:
            # No reraise here, try to keep going
            failures.add('collector %s failed: %s', name, exc)

    resp = call_inspector(data, failures)

    # Now raise everything we were delaying
    failures.raise_if_needed()

    if resp is None:
        LOG.info('stopping inspection, as inspector returned an error')
        return

    LOG.info('inspection finished successfully')
    return resp.get('uuid')
Ejemplo n.º 3
0
    def __init__(self,
                 api_url,
                 advertise_address,
                 listen_address,
                 ip_lookup_attempts,
                 ip_lookup_sleep,
                 network_interface,
                 lookup_timeout,
                 lookup_interval,
                 standalone,
                 hardware_initialization_delay=0):
        super(IronicPythonAgent, self).__init__()
        if bool(cfg.CONF.keyfile) != bool(cfg.CONF.certfile):
            LOG.warning("Only one of 'keyfile' and 'certfile' options is "
                        "defined in config file. Its value will be ignored.")
        self.ext_mgr = extension.ExtensionManager(
            namespace='ironic_python_agent.extensions',
            invoke_on_load=True,
            propagate_map_exceptions=True,
            invoke_kwds={'agent': self},
        )
        self.api_url = api_url
        if not self.api_url or self.api_url == 'mdns':
            try:
                self.api_url, params = mdns.get_endpoint('baremetal')
            except lib_exc.ServiceLookupFailure:
                if self.api_url:
                    # mDNS explicitly requested, report failure.
                    raise
                else:
                    # implicit fallback to mDNS, do not fail (maybe we're only
                    # running inspection).
                    LOG.warning('Could not get baremetal endpoint from mDNS, '
                                'will not heartbeat')
            else:
                config.override(params)

        if self.api_url:
            self.api_client = ironic_api_client.APIClient(self.api_url)
            self.heartbeater = IronicPythonAgentHeartbeater(self)
        self.listen_address = listen_address
        self.advertise_address = advertise_address
        self.version = pkg_resources.get_distribution('ironic-python-agent')\
            .version
        self.api = app.VersionSelectorApplication(self)
        self.heartbeat_timeout = None
        self.started_at = None
        self.node = None
        # lookup timeout in seconds
        self.lookup_timeout = lookup_timeout
        self.lookup_interval = lookup_interval
        self.ip_lookup_attempts = ip_lookup_attempts
        self.ip_lookup_sleep = ip_lookup_sleep
        self.network_interface = network_interface
        self.standalone = standalone
        self.hardware_initialization_delay = hardware_initialization_delay
        # IPA will stop serving requests and exit after this is set to False
        self.serve_api = True
        self.iscsi_started = False
Ejemplo n.º 4
0
    def test_https(self, mock_zc):
        mock_zc.return_value.get_service_info.return_value = mock.Mock(
            address=socket.inet_aton('192.168.1.1'), port=443, properties={})

        endp, params = mdns.get_endpoint('baremetal')
        self.assertEqual('https://192.168.1.1:443', endp)
        self.assertEqual({}, params)
        mock_zc.return_value.get_service_info.assert_called_once_with(
            'baremetal._openstack._tcp.local.',
            'baremetal._openstack._tcp.local.')
Ejemplo n.º 5
0
    def test_with_custom_port_and_path(self, mock_zc):
        mock_zc.return_value.get_service_info.return_value = mock.Mock(
            address=socket.inet_aton('192.168.1.1'),
            port=8080,
            properties={b'path': b'/baremetal'})

        endp, params = mdns.get_endpoint('baremetal')
        self.assertEqual('https://192.168.1.1:8080/baremetal', endp)
        self.assertEqual({}, params)
        mock_zc.return_value.get_service_info.assert_called_once_with(
            'baremetal._openstack._tcp.local.',
            'baremetal._openstack._tcp.local.')
Ejemplo n.º 6
0
    def __init__(self, api_url, advertise_address, listen_address,
                 ip_lookup_attempts, ip_lookup_sleep, network_interface,
                 lookup_timeout, lookup_interval, standalone,
                 hardware_initialization_delay=0):
        super(IronicPythonAgent, self).__init__()
        if bool(cfg.CONF.keyfile) != bool(cfg.CONF.certfile):
            LOG.warning("Only one of 'keyfile' and 'certfile' options is "
                        "defined in config file. Its value will be ignored.")
        self.ext_mgr = extension.ExtensionManager(
            namespace='ironic_python_agent.extensions',
            invoke_on_load=True,
            propagate_map_exceptions=True,
            invoke_kwds={'agent': self},
        )
        self.api_url = api_url
        if not self.api_url or self.api_url == 'mdns':
            try:
                self.api_url, params = mdns.get_endpoint('baremetal')
            except lib_exc.ServiceLookupFailure:
                if self.api_url:
                    # mDNS explicitly requested, report failure.
                    raise
                else:
                    # implicit fallback to mDNS, do not fail (maybe we're only
                    # running inspection).
                    LOG.warning('Could not get baremetal endpoint from mDNS, '
                                'will not heartbeat')
            else:
                config.override(params)

        if self.api_url:
            self.api_client = ironic_api_client.APIClient(self.api_url)
            self.heartbeater = IronicPythonAgentHeartbeater(self)
        self.listen_address = listen_address
        self.advertise_address = advertise_address
        self.version = pkg_resources.get_distribution('ironic-python-agent')\
            .version
        self.api = app.VersionSelectorApplication(self)
        self.heartbeat_timeout = None
        self.started_at = None
        self.node = None
        # lookup timeout in seconds
        self.lookup_timeout = lookup_timeout
        self.lookup_interval = lookup_interval
        self.ip_lookup_attempts = ip_lookup_attempts
        self.ip_lookup_sleep = ip_lookup_sleep
        self.network_interface = network_interface
        self.standalone = standalone
        self.hardware_initialization_delay = hardware_initialization_delay
        # IPA will stop serving requests and exit after this is set to False
        self.serve_api = True
Ejemplo n.º 7
0
    def test_binary_data(self, mock_zc):
        mock_zc.return_value.get_service_info.return_value = mock.Mock(
            address=socket.inet_aton('192.168.1.1'),
            port=80,
            properties={
                b'ipa_debug': True,
                b'binary': b'\xe2\x28\xa1'
            })

        endp, params = mdns.get_endpoint('baremetal')
        self.assertEqual('http://192.168.1.1:80', endp)
        self.assertEqual({
            'ipa_debug': True,
            'binary': b'\xe2\x28\xa1'
        }, params)
        mock_zc.return_value.get_service_info.assert_called_once_with(
            'baremetal._openstack._tcp.local.',
            'baremetal._openstack._tcp.local.')
Ejemplo n.º 8
0
    def __init__(self,
                 api_url,
                 advertise_address,
                 listen_address,
                 ip_lookup_attempts,
                 ip_lookup_sleep,
                 network_interface,
                 lookup_timeout,
                 lookup_interval,
                 standalone,
                 agent_token,
                 hardware_initialization_delay=0,
                 advertise_protocol='http'):
        super(IronicPythonAgent, self).__init__()
        if bool(cfg.CONF.keyfile) != bool(cfg.CONF.certfile):
            LOG.warning("Only one of 'keyfile' and 'certfile' options is "
                        "defined in config file. Its value will be ignored.")
        self.ext_mgr = base.init_ext_manager(self)
        self.api_url = api_url
        if (not self.api_url or self.api_url == 'mdns') and not standalone:
            try:
                self.api_url, params = mdns.get_endpoint('baremetal')
            except lib_exc.ServiceLookupFailure:
                if self.api_url:
                    # mDNS explicitly requested, report failure.
                    raise
                else:
                    # implicit fallback to mDNS, do not fail (maybe we're only
                    # running inspection).
                    LOG.warning('Could not get baremetal endpoint from mDNS, '
                                'will not heartbeat')
            else:
                config.override(params)

        if self.api_url:
            self.api_client = ironic_api_client.APIClient(self.api_url)
            self.heartbeater = IronicPythonAgentHeartbeater(self)
        self.listen_address = listen_address
        self.advertise_address = advertise_address
        self.advertise_protocol = advertise_protocol
        self.version = pkg_resources.get_distribution('ironic-python-agent')\
            .version
        self.api = app.Application(self, cfg.CONF)
        self.heartbeat_timeout = None
        self.started_at = None
        self.node = None
        # lookup timeout in seconds
        self.lookup_timeout = lookup_timeout
        self.lookup_interval = lookup_interval
        self.ip_lookup_attempts = ip_lookup_attempts
        self.ip_lookup_sleep = ip_lookup_sleep
        self.network_interface = network_interface
        self.standalone = standalone
        self.hardware_initialization_delay = hardware_initialization_delay
        # IPA will stop serving requests and exit after this is set to False
        self.serve_api = True
        self.agent_token = agent_token
        # Allows this to be turned on by the conductor while running,
        # in the event of long running ramdisks where the conductor
        # got upgraded somewhere along the way.
        self.agent_token_required = cfg.CONF.agent_token_required
        self.iscsi_started = False