Beispiel #1
0
    def test_buffer(self):
        SetUpTestInjections(metrics_db=':memory:', metrics_db_lock=Lock())
        controller = MetricsCacheController()
        tags = {'name': 'name', 'id': 0}

        expected_metrics = []
        for i in range(10):
            timestamp = 300 + 60 * 60 * 12 * i  # Metric every 12 hours
            controller.buffer_counter('OpenMotics', 'foobar', tags,
                                      {'counter': i}, timestamp)
            if not (i % 2):
                # Only one metric every day is expected to be buffered
                expected_metrics.append({'counter': i, 'timestamp': timestamp})

        buffered_metrics = MetricsTest._load_buffered_metrics(controller)
        self.assertEqual(5, len(buffered_metrics))
        self.assertEqual(expected_metrics, buffered_metrics)

        removed = controller.clear_buffer(60 * 60 * 24 * 2)
        self.assertEqual(2, removed)
        buffered_metrics = MetricsTest._load_buffered_metrics(controller)
        self.assertEqual(3, len(buffered_metrics))
        self.assertEqual(expected_metrics[2:], buffered_metrics)
def main():
    """ Main function. """
    config = ConfigParser()
    config.read(constants.get_config_file())

    defaults = {
        'username': config.get('OpenMotics', 'cloud_user'),
        'password': config.get('OpenMotics', 'cloud_pass')
    }
    controller_serial_port = config.get('OpenMotics', 'controller_serial')
    passthrough_serial_port = config.get('OpenMotics', 'passthrough_serial')
    power_serial_port = config.get('OpenMotics', 'power_serial')
    gateway_uuid = config.get('OpenMotics', 'uuid')

    config_lock = threading.Lock()
    user_controller = UserController(constants.get_config_database_file(),
                                     config_lock, defaults, 3600)
    config_controller = ConfigurationController(
        constants.get_config_database_file(), config_lock)

    led_service = LedService()

    controller_serial = Serial(controller_serial_port, 115200)
    power_serial = RS485(Serial(power_serial_port, 115200, timeout=None))

    master_communicator = MasterCommunicator(controller_serial)

    if passthrough_serial_port:
        passthrough_serial = Serial(passthrough_serial_port, 115200)
        passthrough_service = PassthroughService(master_communicator,
                                                 passthrough_serial)
        passthrough_service.start()

    master_communicator.start(
    )  # A running master_communicator is required for the startup of services below

    power_controller = PowerController(constants.get_power_database_file())
    power_communicator = PowerCommunicator(power_serial, power_controller)

    gateway_api = GatewayApi(master_communicator, power_communicator,
                             power_controller)

    scheduling_controller = SchedulingController(
        constants.get_scheduling_database_file(), config_lock, gateway_api)

    maintenance_service = MaintenanceService(
        gateway_api, constants.get_ssl_private_key_file(),
        constants.get_ssl_certificate_file())

    web_interface = WebInterface(user_controller, gateway_api,
                                 maintenance_service,
                                 led_service.in_authorized_mode,
                                 config_controller, scheduling_controller)

    scheduling_controller.set_webinterface(web_interface)

    plugin_controller = PluginController(web_interface, config_controller)

    web_interface.set_plugin_controller(plugin_controller)
    gateway_api.set_plugin_controller(plugin_controller)

    # Metrics
    metrics_cache_controller = MetricsCacheController(
        constants.get_metrics_database_file(), threading.Lock())
    metrics_collector = MetricsCollector(gateway_api)
    metrics_controller = MetricsController(plugin_controller,
                                           metrics_collector,
                                           metrics_cache_controller,
                                           config_controller, gateway_uuid)
    metrics_collector.set_controllers(metrics_controller, plugin_controller)
    metrics_collector.set_plugin_intervals(plugin_controller.metric_intervals)
    metrics_controller.add_receiver(metrics_controller.receiver)
    metrics_controller.add_receiver(web_interface.distribute_metric)

    plugin_controller.set_metrics_controller(metrics_controller)
    web_interface.set_metrics_collector(metrics_collector)
    web_interface.set_metrics_controller(metrics_controller)

    web_service = WebService(web_interface, config_controller)

    def _on_output(*args, **kwargs):
        metrics_collector.on_output(*args, **kwargs)
        gateway_api.on_outputs(*args, **kwargs)

    def _on_input(*args, **kwargs):
        metrics_collector.on_input(*args, **kwargs)
        gateway_api.on_inputs(*args, **kwargs)

    master_communicator.register_consumer(
        BackgroundConsumer(master_api.output_list(), 0, _on_output, True))
    master_communicator.register_consumer(
        BackgroundConsumer(master_api.input_list(), 0, _on_input))

    power_communicator.start()
    plugin_controller.start_plugins()
    metrics_controller.start()
    scheduling_controller.start()
    metrics_collector.start()
    web_service.start()

    led_thread = threading.Thread(target=led_driver,
                                  args=(led_service, master_communicator,
                                        power_communicator))
    led_thread.setName("Serial led driver thread")
    led_thread.daemon = True
    led_thread.start()

    def stop(signum, frame):
        """ This function is called on SIGTERM. """
        _ = signum, frame
        sys.stderr.write("Shutting down")
        web_service.stop()
        metrics_collector.stop()
        metrics_controller.stop()
        plugin_controller.stop()

    signal(SIGTERM, stop)
Beispiel #3
0
    def test_metrics_receiver(self):

        Config.set_entry('cloud_endpoint', 'tests.openmotics.com')
        Config.set_entry('cloud_endpoint_metrics', 'metrics')
        Config.set_entry('cloud_metrics_interval|foobar', 5)

        # Add interceptors

        send_metrics = []
        response_data = {}

        def post(url, data, timeout):
            _ = url, timeout
            # Extract metrics, parse assumed data format
            time.sleep(1)
            send_metrics.append([m[0] for m in json.loads(data['metrics'])])
            response = type('response', (), {})()
            response.text = json.dumps(copy.deepcopy(response_data))
            return response

        # Initialize (mocked) classes

        base_metric = {
            'source': 'OpenMotics',
            'type': 'foobar',
            'timestamp': 1,
            'tags': {
                'name': 'name',
                'id': 0
            },
            'values': {
                'counter': 0
            }
        }

        requests.post = post

        SetUpTestInjections(metrics_db=':memory:', metrics_db_lock=Lock())

        metrics_cache = MetricsCacheController()
        metrics_collector_mock = Mock()
        metrics_collector_mock.intervals = []

        definitions = [{
            'type':
            'foobar',
            'tags': ['id', 'name'],
            'metrics': [{
                'name': 'counter',
                'description': 'Some field',
                'type': 'counter',
                'policies': ['buffer'],
                'unit': ''
            }]
        }]
        metrics_collector_mock.get_definitions = lambda: definitions

        SetUpTestInjections(plugin_controller=Mock(),
                            metrics_collector=metrics_collector_mock,
                            metrics_cache_controller=metrics_cache,
                            gateway_uuid='uuid')

        metrics_controller = MetricsController()
        metrics_controller._needs_upload_to_cloud = lambda *args, **kwargs: True
        self.assertEqual(metrics_controller._buffer_counters,
                         {'OpenMotics': {
                             'foobar': {
                                 'counter': True
                             }
                         }})

        # Add some helper methods

        def send_metric(counter, error):
            response_data.update({'success': True})
            if error:
                response_data.update({'success': False, 'error': 'error'})
            metric = copy.deepcopy(base_metric)
            # noinspection PyTypeChecker
            metric['timestamp'] = time.time()
            metric['values']['counter'] = counter
            metrics_controller.receiver(metric)
            return metric

        def assert_fields(controller, cache, queue, stats, buffer, last_send,
                          last_try, retry_interval):
            self.assertDictEqual(controller._cloud_cache, cache)
            self.assertListEqual(controller._cloud_queue, queue)
            self.assertDictEqual(controller.cloud_stats, stats)
            self.assertListEqual(controller._cloud_buffer, buffer)
            self.assertEqual(controller._cloud_last_send, last_send)
            self.assertEqual(controller._cloud_last_try, last_try)
            self.assertEqual(controller._cloud_retry_interval, retry_interval)

        # Validate initial state

        assert_fields(metrics_controller,
                      cache={},
                      queue=[],
                      stats={
                          'queue': 0,
                          'buffer': 0,
                          'time_ago_send': 0,
                          'time_ago_try': 0
                      },
                      buffer=[],
                      last_send=0,
                      last_try=0,
                      retry_interval=None)

        logger.info('Send first metrics, but raise exception on "cloud"')

        send_metrics = []
        Config.set_entry('cloud_metrics_batch_size', 0)
        Config.set_entry('cloud_metrics_min_interval', 0)

        time.sleep(10)  # Time moves on inside fakesleep
        metric_1 = send_metric(counter=0, error=True)
        buffer_metric_timestamp = metric_1['timestamp']

        self.assertEqual(len(send_metrics), 1)
        metrics = send_metrics.pop()
        self.assertEqual(len(metrics), 1)
        self.assertDictEqual(metrics.pop(), metric_1)

        assert_fields(
            metrics_controller,
            cache={
                'OpenMotics': {
                    'foobar': {
                        'id=0|name=name': {
                            'timestamp': 10
                        }
                    }
                }
            },
            queue=[[metric_1]],
            stats={
                'queue': 1,
                'buffer': 0,
                'time_ago_send': 10,
                'time_ago_try': 10
            },  # Nothing buffered yet
            buffer=[],
            last_send=0,
            last_try=10,
            retry_interval=0)
        buffered_metrics = MetricsTest._load_buffered_metrics(metrics_cache)
        self.assertEqual(buffered_metrics, [{
            'timestamp': buffer_metric_timestamp,
            'counter': 0
        }])

        logger.info('Send another metric, still errors on "cloud"')

        time.sleep(10)  # Time moves on inside fakesleep
        metric_2 = send_metric(counter=1, error=True)

        self.assertEqual(len(send_metrics), 1)
        metrics = send_metrics.pop()
        self.assertEqual(len(metrics), 2)
        self.assertDictEqual(metrics.pop(), metric_2)
        self.assertDictEqual(metrics.pop(), metric_1)

        assert_fields(metrics_controller,
                      cache={
                          'OpenMotics': {
                              'foobar': {
                                  'id=0|name=name': {
                                      'timestamp': 20
                                  }
                              }
                          }
                      },
                      queue=[[metric_1], [metric_2]],
                      stats={
                          'queue': 2,
                          'buffer': 1,
                          'time_ago_send': 21,
                          'time_ago_try': 11
                      },
                      buffer=[],
                      last_send=0,
                      last_try=21,
                      retry_interval=0)
        buffered_metrics = MetricsTest._load_buffered_metrics(metrics_cache)
        self.assertEqual(buffered_metrics, [{
            'timestamp': buffer_metric_timestamp,
            'counter': 0
        }])

        logger.info(
            'Send another metric, this time the call is accepted correctly')

        time.sleep(10)  # Time moves on inside fakesleep
        metric_3 = send_metric(counter=2, error=False)

        self.assertEqual(len(send_metrics), 1)
        metrics = send_metrics.pop()
        self.assertEqual(len(metrics), 3)
        self.assertDictEqual(metrics.pop(), metric_3)
        self.assertDictEqual(metrics.pop(), metric_2)
        self.assertDictEqual(metrics.pop(), metric_1)

        assert_fields(
            metrics_controller,
            cache={
                'OpenMotics': {
                    'foobar': {
                        'id=0|name=name': {
                            'timestamp': 30
                        }
                    }
                }
            },
            queue=[],
            stats={
                'queue': 3,
                'buffer': 1,
                'time_ago_send': 32,
                'time_ago_try': 11
            },  # Buffer stats not cleared yet
            buffer=[],
            last_send=32,
            last_try=32,
            retry_interval=0)
        buffered_metrics = MetricsTest._load_buffered_metrics(metrics_cache)
        self.assertEqual(buffered_metrics, [])

        logger.info('Send another metrics, with increased batch sizes')

        send_metrics = []
        Config.set_entry('cloud_metrics_batch_size', 3)
        Config.set_entry('cloud_metrics_min_interval', 300)

        time.sleep(10)  # Time moves on inside fakesleep
        metric_1 = send_metric(counter=3, error=False)
        time.sleep(1)  # Time moves on inside fakesleep
        send_metric(
            counter=4, error=False
        )  # This metric has the same (rounded) timestamp, so should be discarded
        time.sleep(9)  # Time moves on inside fakesleep
        metric_2 = send_metric(counter=5, error=False)

        self.assertEqual(len(send_metrics),
                         0)  # No metric send, still < batch size

        assert_fields(metrics_controller,
                      cache={
                          'OpenMotics': {
                              'foobar': {
                                  'id=0|name=name': {
                                      'timestamp': 50
                                  }
                              }
                          }
                      },
                      queue=[[metric_1], [metric_2]],
                      stats={
                          'queue': 2,
                          'buffer': 0,
                          'time_ago_send': 21,
                          'time_ago_try': 21
                      },
                      buffer=[],
                      last_send=32,
                      last_try=32,
                      retry_interval=300)
        buffered_metrics = MetricsTest._load_buffered_metrics(metrics_cache)
        self.assertEqual(buffered_metrics, [])

        time.sleep(10)  # Time moves on inside fakesleep
        metric_3 = send_metric(
            counter=6,
            error=False)  # Add another metric, now reaching batch size

        self.assertEqual(len(send_metrics), 1)
        metrics = send_metrics.pop()
        self.assertEqual(len(metrics), 3)
        self.assertDictEqual(metrics.pop(), metric_3)
        self.assertDictEqual(metrics.pop(), metric_2)
        self.assertDictEqual(metrics.pop(), metric_1)
        self.assertEqual(len(metrics), 0)

        assert_fields(metrics_controller,
                      cache={
                          'OpenMotics': {
                              'foobar': {
                                  'id=0|name=name': {
                                      'timestamp': 60
                                  }
                              }
                          }
                      },
                      queue=[],
                      stats={
                          'queue': 3,
                          'buffer': 0,
                          'time_ago_send': 31,
                          'time_ago_try': 31
                      },
                      buffer=[],
                      last_send=63,
                      last_try=63,
                      retry_interval=300)
        buffered_metrics = MetricsTest._load_buffered_metrics(metrics_cache)
        self.assertEqual(buffered_metrics, [])

        logger.info(
            'Send metric after minimum interval, even though batch size isn\'t reached'
        )

        time.sleep(300)  # Time moves on inside fakesleep
        metric_1 = send_metric(
            counter=6,
            error=False)  # Add another metric, now reaching batch size

        self.assertEqual(len(send_metrics), 1)
        metrics = send_metrics.pop()
        self.assertListEqual(metrics, [metric_1])

        assert_fields(metrics_controller,
                      cache={
                          'OpenMotics': {
                              'foobar': {
                                  'id=0|name=name': {
                                      'timestamp': 360
                                  }
                              }
                          }
                      },
                      queue=[],
                      stats={
                          'queue': 1,
                          'buffer': 0,
                          'time_ago_send': 301,
                          'time_ago_try': 301
                      },
                      buffer=[],
                      last_send=364,
                      last_try=364,
                      retry_interval=300)
        buffered_metrics = MetricsTest._load_buffered_metrics(metrics_cache)
        self.assertEqual(buffered_metrics, [])

        logger.info('Send metric, but raise exception on "cloud"')

        send_metrics = []
        Config.set_entry('cloud_metrics_batch_size', 0)

        time.sleep(10)  # Time moves on inside fakesleep
        metric_1 = send_metric(counter=7, error=True)
        buffer_metric_timestamp = metric_1['timestamp']

        self.assertEqual(len(send_metrics), 1)
        metrics = send_metrics.pop()
        self.assertEqual(len(metrics), 1)
        self.assertDictEqual(metrics.pop(), metric_1)

        assert_fields(
            metrics_controller,
            cache={
                'OpenMotics': {
                    'foobar': {
                        'id=0|name=name': {
                            'timestamp': 375
                        }
                    }
                }
            },
            queue=[[metric_1]],
            stats={
                'queue': 1,
                'buffer': 0,
                'time_ago_send': 11,
                'time_ago_try': 11
            },  # Nothing buffered yet
            buffer=[],
            last_send=364,
            last_try=375,
            retry_interval=300)
        buffered_metrics = MetricsTest._load_buffered_metrics(metrics_cache)
        self.assertEqual(buffered_metrics, [{
            'timestamp': buffer_metric_timestamp,
            'counter': 7
        }])

        # Emulate service restart

        metrics_controller = MetricsController()
        metrics_controller._needs_upload_to_cloud = lambda *args, **kwargs: True

        # Validate startup state

        assert_fields(metrics_controller,
                      cache={},
                      queue=[],
                      stats={
                          'queue': 0,
                          'buffer': 1,
                          'time_ago_send': 0,
                          'time_ago_try': 0
                      },
                      buffer=[[metric_1]],
                      last_send=376,
                      last_try=376,
                      retry_interval=None)
        buffered_metrics = MetricsTest._load_buffered_metrics(metrics_cache)
        self.assertEqual(buffered_metrics, [{
            'timestamp': buffer_metric_timestamp,
            'counter': 7
        }])

        logger.info(
            'Send another metric which should result in sending queue en buffer'
        )

        time.sleep(10)  # Time moves on inside fakesleep
        metric_2 = send_metric(counter=8, error=False)

        self.assertEqual(len(send_metrics), 1)
        metrics = send_metrics.pop()
        self.assertEqual(len(metrics), 2)
        self.assertDictEqual(metrics.pop(), metric_2)
        self.assertDictEqual(metrics.pop(), metric_1)

        assert_fields(metrics_controller,
                      cache={
                          'OpenMotics': {
                              'foobar': {
                                  'id=0|name=name': {
                                      'timestamp': 385
                                  }
                              }
                          }
                      },
                      queue=[],
                      stats={
                          'queue': 1,
                          'buffer': 1,
                          'time_ago_send': 10,
                          'time_ago_try': 10
                      },
                      buffer=[],
                      last_send=386,
                      last_try=386,
                      retry_interval=300)
        buffered_metrics = MetricsTest._load_buffered_metrics(metrics_cache)
        self.assertEqual(buffered_metrics, [])