示例#1
0
    def _send_payload(self):
        """
        Sends current payload to backend
        """
        context.log.debug(
            'modified payload; current payload stats: '
            'meta - %s, metrics - %s, events - %s, configs - %s' %
            (len(self.payload['meta']), len(self.payload['metrics']),
             len(self.payload['events']), len(self.payload['configs'])))

        # Send payload to backend.
        try:
            self.last_http_attempt = time.time()

            self._pre_process_payload()  # Convert deques to lists for encoding
            context.http_client.post('update/', data=self.payload)
            context.default_log.debug(self.payload)
            self._reset_payload()  # Clear payload after successful

            if self.first_run:
                self.first_run = False  # Set first_run to False after first successful send

            if self.http_delay:
                self.http_fail_count = 0
                self.http_delay = 0  # Reset HTTP delay on success
                context.log.debug('successful update, reset http delay')
        except Exception as e:
            self._post_process_payload(
            )  # Convert lists to deques since send failed

            if isinstance(e, HTTPError) and e.response.status_code == 503:
                backpressure_error = HTTP503Error(e)
                context.backpressure_time = int(time.time() +
                                                backpressure_error.delay)
                context.log.debug(
                    'back pressure delay %s added (next talk: %s)' %
                    (backpressure_error.delay, context.backpressure_time))
            else:
                self.http_fail_count += 1
                self.http_delay = exponential_delay(self.http_fail_count)
                context.log.debug('http delay set to %s (fails: %s)' %
                                  (self.http_delay, self.http_fail_count))

            exception_name = e.__class__.__name__
            context.log.error('failed to push data due to %s' % exception_name)
            context.log.debug('additional info:', exc_info=True)

        context.log.debug(
            'finished flush_all; new payload stats: '
            'meta - %s, metrics - %s, events - %s, configs - %s' %
            (len(self.payload['meta']), len(self.payload['metrics']),
             len(self.payload['events']), len(self.payload['configs'])))
    def talk_to_cloud(self, root_object=None, force=False, initial=False):
        """
        Asks cloud for config, object configs, filters, etc
        Applies gathered data to objects and agent config

        :param root_object: {} definition dict of a top object
        :param force: bool will skip time check
        :param initial: bool first run
        """
        now = int(time.time())
        if not force and (now <=
                          (self.last_cloud_talk_time +
                           context.app_config['cloud']['talk_interval'] +
                           self.cloud_talk_delay)
                          or now < context.backpressure_time):
            return

        # Handle root_object before explicitly initializing a root object
        if not root_object:
            root_object = get_root_definition()

        # talk to cloud
        try:
            # reset the cloud talk counter to avoid sending new requests every 5.0 seconds
            self.last_cloud_talk_time = int(time.time())

            cloud_response = CloudResponse(
                context.http_client.post('agent/', data=root_object))

            if self.cloud_talk_delay:
                self.cloud_talk_fails = 0
                self.cloud_talk_delay = 0
                context.log.debug(
                    'successful cloud connect, reset cloud talk delay')
        except Exception as e:
            if isinstance(e, HTTPError) and e.response.status_code == 503:
                backpressure_error = HTTP503Error(e)
                context.backpressure_time = int(time.time() +
                                                backpressure_error.delay)
                context.log.debug(
                    'back pressure delay %s added (next talk: %s)' %
                    (backpressure_error.delay, context.backpressure_time))
            else:
                self.cloud_talk_fails += 1
                self.cloud_talk_delay = exponential_delay(
                    self.cloud_talk_fails)
                context.log.debug(
                    'cloud talk delay set to %s (fails: %s)' %
                    (self.cloud_talk_delay, self.cloud_talk_fails))

            context.log.error('could not connect to cloud', exc_info=True)
            raise AmplifyCriticalException()

        # check agent version status
        if context.version_semver <= cloud_response.versions.obsolete:
            context.log.error(
                'agent is obsolete - cloud will refuse updates until it is updated (version: %s, current: %s)'
                % (tuple_to_version(context.version_semver),
                   tuple_to_version(cloud_response.versions.current)))
            self.stop()
        elif context.version_semver <= cloud_response.versions.old:
            context.log.warn(
                'agent is old - update is recommended (version: %s, current: %s)'
                % (tuple_to_version(context.version_semver),
                   tuple_to_version(cloud_response.versions.current)))

        # set capabilities
        for name, status in cloud_response.capabilities.iteritems():
            name = ''.join([char.lower() for char in name if char.isalpha()])
            context.capabilities[name] = status

        # update special object configs and filters
        changed_object_managers = set()
        matched_object_configs = set()
        for obj in cloud_response.objects:
            object_manager = self.object_managers.get(obj.type)
            if object_manager is None:
                continue

            if obj.id in object_manager.object_configs:
                matched_object_configs.add(obj.id)

            if object_manager.object_configs.get(obj.id, {}) != obj.config:
                context.log.info(
                    'object config has changed. now "%s" %s is running with: %s'
                    % (obj.type, obj.id, pprint.pformat(obj.config)))
                object_manager.object_configs[obj.id] = obj.config
                changed_object_managers.add(obj.type)
                matched_object_configs.add(obj.id)

        # purge obsoleted object configs
        for object_type, object_manager in self.object_managers.iteritems():
            for obj_id in object_manager.object_configs.keys():
                if obj_id not in matched_object_configs:
                    context.log.debug(
                        'object config has changed. now "%s" %s is running with default settings'
                        % (object_type, obj_id))
                    del object_manager.object_configs[obj_id]
                    changed_object_managers.add(object_type)

        # don't change api_url if a custom url was set by the user in the agent config
        if context.freeze_api_url:
            cloud_response.config.get('cloud', {}).pop('api_url', None)

        # global config changes
        def _recursive_dict_match_only_existing(kwargs1, kwargs2):
            for k, v1 in kwargs1.iteritems():
                if isinstance(v1, dict):
                    v2 = kwargs2.get(k, {})

                    if not isinstance(v2, dict):
                        return False

                    if not _recursive_dict_match_only_existing(
                            v1, kwargs2.get(k, {})):
                        return False
                else:
                    if v1 != kwargs2.get(str(k)):
                        return False
            return True

        config_changed = not _recursive_dict_match_only_existing(
            cloud_response.config, context.app_config.default)

        # apply new config
        context.app_config.apply(cloud_response.config, target=0)

        # perform restarts
        if config_changed or len(changed_object_managers) > 0:
            context.cloud_restart = True
            if self.bridge_object:
                self.bridge_object.flush_metrics()

            if config_changed:
                context.log.debug(
                    'app config has changed. now running with: %s' %
                    pprint.pformat(context.app_config.config))

                context.http_client.update_cloud_url()

                if self.object_managers:
                    for object_manager_name in reversed(
                            self.object_manager_order):
                        object_manager = self.object_managers[
                            object_manager_name]
                        object_manager.stop()

                    for object_manager_name in self.external_object_manager_types:
                        object_manager = self.object_managers[
                            object_manager_name]
                        object_manager.stop()

                    for name in self.external_managers.keys():
                        attr_string = '%s_manager' % name
                        thread = getattr(self, attr_string, None)
                        if thread is not None:
                            thread.kill()

            elif len(changed_object_managers) > 0:
                context.log.debug('obj configs changed. changed managers: %s' %
                                  list(changed_object_managers))
                for obj_type in changed_object_managers:
                    self.object_managers[obj_type].stop()

            if not initial:
                self.init_object_managers()
                self.load_ext_managers()

            self.last_cloud_talk_restart = int(time.time())
            context.cloud_restart = False