Пример #1
0
    def run(self):
        """Run the Ironic Python Agent."""
        LOG.info('Starting ironic-python-agent version: %s', self.version)
        # Get the UUID so we can heartbeat to Ironic. Raises LookupNodeError
        # if there is an issue (uncaught, restart agent)
        self.started_at = _time()
        # Attempt to sync the software clock
        utils.sync_clock(ignore_errors=True)

        # Cached hw managers at runtime, not load time. See bug 1490008.
        hardware.get_managers()
        # Operator-settable delay before hardware actually comes up.
        # Helps with slow RAID drivers - see bug 1582797.
        if self.hardware_initialization_delay > 0:
            LOG.info('Waiting %d seconds before proceeding',
                     self.hardware_initialization_delay)
            time.sleep(self.hardware_initialization_delay)

        if not self.standalone:
            # Inspection should be started before call to lookup, otherwise
            # lookup will fail due to unknown MAC.
            uuid = None
            # We can't try to inspect or heartbeat until we have valid
            # interfaces to perform those actions over.
            self._wait_for_interface()

            if cfg.CONF.inspection_callback_url:
                try:
                    # Attempt inspection. This may fail, and previously
                    # an error would be logged.
                    uuid = inspector.inspect()
                except errors.InspectionError as e:
                    LOG.error('Failed to perform inspection: %s', e)

            if self.api_url:
                content = self.api_client.lookup_node(
                    hardware_info=hardware.list_hardware_info(use_cache=True),
                    timeout=self.lookup_timeout,
                    starting_interval=self.lookup_interval,
                    node_uuid=uuid)
                LOG.debug('Received lookup results: %s', content)
                self.process_lookup_data(content)
                # Save the API url in case we need it later.
                hardware.save_api_client(self.api_client, self.lookup_timeout,
                                         self.lookup_interval)

            elif cfg.CONF.inspection_callback_url:
                LOG.info('No ipa-api-url configured, Heartbeat and lookup '
                         'skipped for inspector.')
            else:
                # NOTE(TheJulia): Once communication flow capability is
                # able to be driven solely from the conductor, this is no
                # longer a major issue.
                LOG.error('Neither ipa-api-url nor inspection_callback_url'
                          'found, please check your pxe append parameters.')

        self.serve_ipa_api()

        if not self.standalone and self.api_url:
            self.heartbeater.stop()
Пример #2
0
 def test_sync_clock_chrony(self, mock_time_method, mock_execute):
     self.config(ntp_server='192.168.1.1')
     mock_time_method.return_value = 'chronyd'
     utils.sync_clock()
     mock_execute.assert_has_calls([
         mock.call('chronyd', check_exit_code=[0, 1]),
         mock.call('chronyc', 'add', 'server', '192.168.1.1'),
         mock.call('chronyc', 'makestep'),
     ])
Пример #3
0
 def test_sync_clock_chrony_already_present(self, mock_time_method,
                                            mock_execute):
     self.config(ntp_server='192.168.1.1')
     mock_time_method.return_value = 'chronyd'
     mock_execute.side_effect = [
         ('', ''),
         processutils.ProcessExecutionError(
             stderr='Source already present'),
         ('', ''),
     ]
     utils.sync_clock()
     mock_execute.assert_has_calls([
         mock.call('chronyd', check_exit_code=[0, 1]),
         mock.call('chronyc', 'add', 'server', '192.168.1.1'),
         mock.call('chronyc', 'makestep'),
     ])
Пример #4
0
    def _sync_clock(self, ignore_errors=False):
        """Sync the clock to a configured NTP server.

        :param ignore_errors: Boolean option to indicate if the
                              errors should be fatal. This option
                              does not override the fail_if_clock_not_set
                              configuration option.
        :raises: ClockSyncError if a failure is encountered and
                 errors are not ignored.
        """
        try:
            utils.sync_clock(ignore_errors=ignore_errors)
            # Sync the system hardware clock from the software clock,
            # as they are independent and the HW clock can still drift
            # with long running ramdisks.
            utils.execute('hwclock', '-v', '--systohc')
        except (processutils.ProcessExecutionError,
                errors.CommandExecutionError) as e:
            msg = 'Failed to sync hardware clock: %s' % e
            LOG.error(msg)
            if CONF.fail_if_clock_not_set or not ignore_errors:
                raise errors.ClockSyncError(msg)
Пример #5
0
    def run(self):
        """Run the Ironic Python Agent."""
        LOG.info('Starting ironic-python-agent version: %s', self.version)
        # Get the UUID so we can heartbeat to Ironic. Raises LookupNodeError
        # if there is an issue (uncaught, restart agent)
        self.started_at = _time()
        # Attempt to sync the software clock
        utils.sync_clock(ignore_errors=True)

        # Cached hw managers at runtime, not load time. See bug 1490008.
        hardware.get_managers()
        # Operator-settable delay before hardware actually comes up.
        # Helps with slow RAID drivers - see bug 1582797.
        if self.hardware_initialization_delay > 0:
            LOG.info('Waiting %d seconds before proceeding',
                     self.hardware_initialization_delay)
            time.sleep(self.hardware_initialization_delay)

        if not self.standalone:
            # Inspection should be started before call to lookup, otherwise
            # lookup will fail due to unknown MAC.
            uuid = None
            if cfg.CONF.inspection_callback_url:
                try:
                    # Attempt inspection. This may fail, and previously
                    # an error would be logged.
                    uuid = inspector.inspect()
                except errors.InspectionError as e:
                    LOG.error('Failed to perform inspection: %s', e)

            if self.api_url:
                self._wait_for_interface()
                content = self.api_client.lookup_node(
                    hardware_info=hardware.dispatch_to_managers(
                        'list_hardware_info'),
                    timeout=self.lookup_timeout,
                    starting_interval=self.lookup_interval,
                    node_uuid=uuid)

                LOG.debug('Received lookup results: %s', content)
                self.node = content['node']
                LOG.info('Lookup succeeded, node UUID is %s',
                         self.node['uuid'])
                hardware.cache_node(self.node)
                self.heartbeat_timeout = content['config']['heartbeat_timeout']

                # Update config with values from Ironic
                config = content.get('config', {})
                if config.get('metrics'):
                    for opt, val in config.items():
                        setattr(cfg.CONF.metrics, opt, val)
                if config.get('metrics_statsd'):
                    for opt, val in config.items():
                        setattr(cfg.CONF.metrics_statsd, opt, val)
                if config.get('agent_token_required'):
                    self.agent_token_required = True
                token = config.get('agent_token')
                if token:
                    if len(token) >= 32:
                        LOG.debug('Agent token recorded as designated by '
                                  'the ironic installation.')
                        self.agent_token = token
                        # set with-in the API client.
                        self.api_client.agent_token = token
                    elif token == '******':
                        LOG.warning('The agent token has already been '
                                    'retrieved. IPA may not operate as '
                                    'intended and the deployment may fail '
                                    'depending on settings in the ironic '
                                    'deployment.')
                        if not self.agent_token and self.agent_token_required:
                            LOG.error('Ironic is signaling that agent tokens '
                                      'are required, however we do not have '
                                      'a token on file. '
                                      'This is likely **FATAL**.')
                    else:
                        LOG.info('An invalid token was received.')
                if self.agent_token:
                    # Explicitly set the token in our API client before
                    # starting heartbeat operations.
                    self.api_client.agent_token = self.agent_token

            elif cfg.CONF.inspection_callback_url:
                LOG.info('No ipa-api-url configured, Heartbeat and lookup '
                         'skipped for inspector.')
            else:
                LOG.error('Neither ipa-api-url nor inspection_callback_url'
                          'found, please check your pxe append parameters.')

        self.serve_ipa_api()

        if not self.standalone and self.api_url:
            self.heartbeater.stop()
Пример #6
0
 def test_sync_clock_ntp_server_is_none(self, mock_time_method,
                                        mock_execute):
     self.config(ntp_server=None)
     mock_time_method.return_value = None
     utils.sync_clock()
     self.assertEqual(0, mock_execute.call_count)
Пример #7
0
 def test_sync_clock_none(self, mock_time_method, mock_execute):
     self.config(ntp_server='192.168.1.1')
     mock_time_method.return_value = None
     utils.sync_clock(ignore_errors=True)
     self.assertEqual(0, mock_execute.call_count)
Пример #8
0
 def test_sync_clock_ntp(self, mock_time_method, mock_execute):
     self.config(ntp_server='192.168.1.1')
     mock_time_method.return_value = 'ntpdate'
     utils.sync_clock()
     mock_execute.assert_has_calls([mock.call('ntpdate', '192.168.1.1')])